Loading core/java/android/net/NetworkStats.java +110 −25 Original line number Diff line number Diff line Loading @@ -43,17 +43,41 @@ public class NetworkStats implements Parcelable { /** {@link #tag} value for without tag. */ public static final int TAG_NONE = 0; // TODO: move public fields to Entry accessors, then undeprecate // TODO: refactor rx/tx to rxBytes/txBytes /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ @Deprecated public final long elapsedRealtime; @Deprecated public int size; @Deprecated public String[] iface; @Deprecated public int[] uid; @Deprecated public int[] tag; @Deprecated public long[] rx; @Deprecated public long[] rxPackets; @Deprecated public long[] tx; @Deprecated public long[] txPackets; public static class Entry { public String iface; public int uid; public int tag; public long rxBytes; public long rxPackets; public long txBytes; public long txPackets; } public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; Loading @@ -62,7 +86,9 @@ public class NetworkStats implements Parcelable { this.uid = new int[initialSize]; this.tag = new int[initialSize]; this.rx = new long[initialSize]; this.rxPackets = new long[initialSize]; this.tx = new long[initialSize]; this.txPackets = new long[initialSize]; } public NetworkStats(Parcel parcel) { Loading @@ -72,38 +98,82 @@ public class NetworkStats implements Parcelable { uid = parcel.createIntArray(); tag = parcel.createIntArray(); rx = parcel.createLongArray(); rxPackets = parcel.createLongArray(); tx = parcel.createLongArray(); txPackets = parcel.createLongArray(); } /** * Add new stats entry with given values. */ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { final Entry entry = new Entry(); entry.iface = iface; entry.uid = uid; entry.tag = tag; entry.rxBytes = rx; entry.txBytes = tx; return addValues(entry); } /** * Add new stats entry, copying from given {@link Entry}. The {@link Entry} * object can be recycled across multiple calls. */ public NetworkStats addValues(Entry entry) { if (size >= this.iface.length) { final int newLength = Math.max(this.iface.length, 10) * 3 / 2; this.iface = Arrays.copyOf(this.iface, newLength); this.uid = Arrays.copyOf(this.uid, newLength); this.tag = Arrays.copyOf(this.tag, newLength); this.rx = Arrays.copyOf(this.rx, newLength); this.tx = Arrays.copyOf(this.tx, newLength); } this.iface[size] = iface; this.uid[size] = uid; this.tag[size] = tag; this.rx[size] = rx; this.tx[size] = tx; final int newLength = Math.max(iface.length, 10) * 3 / 2; iface = Arrays.copyOf(iface, newLength); uid = Arrays.copyOf(uid, newLength); tag = Arrays.copyOf(tag, newLength); rx = Arrays.copyOf(rx, newLength); rxPackets = Arrays.copyOf(rxPackets, newLength); tx = Arrays.copyOf(tx, newLength); txPackets = Arrays.copyOf(txPackets, newLength); } iface[size] = entry.iface; uid[size] = entry.uid; tag[size] = entry.tag; rx[size] = entry.rxBytes; rxPackets[size] = entry.rxPackets; tx[size] = entry.txBytes; txPackets[size] = entry.txPackets; size++; return this; } /** * Return specific stats entry. */ public Entry getValues(int i, Entry recycle) { final Entry entry = recycle != null ? recycle : new Entry(); entry.iface = iface[i]; entry.uid = uid[i]; entry.tag = tag[i]; entry.rxBytes = rx[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txPackets = txPackets[i]; return entry; } public long getElapsedRealtime() { return elapsedRealtime; } public int size() { return size; } /** * Combine given values with an existing row, or create a new row if * {@link #findIndex(String, int, int)} is unable to find match. Can also be * used to subtract values from existing rows. */ public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { // TODO: extent to accept rxPackets/txPackets final int i = findIndex(iface, uid, tag); if (i == -1) { // only create new entry when positive contribution Loading Loading @@ -199,30 +269,41 @@ public class NetworkStats implements Parcelable { } // result will have our rows, and elapsed time between snapshots final Entry entry = new Entry(); final NetworkStats result = new NetworkStats(deltaRealtime, size); for (int i = 0; i < size; i++) { final String iface = this.iface[i]; final int uid = this.uid[i]; final int tag = this.tag[i]; entry.iface = iface[i]; entry.uid = uid[i]; entry.tag = tag[i]; // find remote row that matches, and subtract final int j = value.findIndex(iface, uid, tag); final int j = value.findIndex(entry.iface, entry.uid, entry.tag); if (j == -1) { // newly appearing row, return entire value result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]); entry.rxBytes = rx[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txPackets = txPackets[i]; } else { // existing row, subtract remote value long rx = this.rx[i] - value.rx[j]; long tx = this.tx[i] - value.tx[j]; if (enforceMonotonic && (rx < 0 || tx < 0)) { entry.rxBytes = rx[i] - value.rx[j]; entry.rxPackets = rxPackets[i] - value.rxPackets[j]; entry.txBytes = tx[i] - value.tx[j]; entry.txPackets = txPackets[i] - value.txPackets[j]; if (enforceMonotonic && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0)) { throw new IllegalArgumentException("found non-monotonic values"); } if (clampNegative) { rx = Math.max(0, rx); tx = Math.max(0, tx); entry.rxBytes = Math.max(0, entry.rxBytes); entry.rxPackets = Math.max(0, entry.rxPackets); entry.txBytes = Math.max(0, entry.txBytes); entry.txPackets = Math.max(0, entry.txPackets); } result.addEntry(iface, uid, tag, rx, tx); } result.addValues(entry); } return result; Loading @@ -235,13 +316,15 @@ public class NetworkStats implements Parcelable { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); for (int i = 0; i < iface.length; i++) { for (int i = 0; i < size; i++) { 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(" rx="); pw.print(rx[i]); pw.print(" tx="); pw.println(tx[i]); pw.print(" rxBytes="); pw.print(rx[i]); pw.print(" rxPackets="); pw.print(rxPackets[i]); pw.print(" txBytes="); pw.print(tx[i]); pw.print(" txPackets="); pw.println(txPackets[i]); } } Loading @@ -265,7 +348,9 @@ public class NetworkStats implements Parcelable { dest.writeIntArray(uid); dest.writeIntArray(tag); dest.writeLongArray(rx); dest.writeLongArray(rxPackets); dest.writeLongArray(tx); dest.writeLongArray(txPackets); } public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { Loading core/tests/coretests/src/android/net/NetworkStatsTest.java +3 −3 Original line number Diff line number Diff line Loading @@ -44,20 +44,20 @@ public class NetworkStatsTest extends TestCase { public void testAddEntryGrow() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 2); assertEquals(0, stats.size); assertEquals(0, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L); assertEquals(2, stats.size); assertEquals(2, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L); assertEquals(5, stats.size); assertEquals(5, stats.size()); assertTrue(stats.iface.length >= 5); assertEquals(1L, stats.rx[0]); Loading services/java/com/android/server/NetworkManagementService.java +111 −27 Original line number Diff line number Diff line Loading @@ -77,11 +77,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { /** Path to {@code /proc/uid_stat}. */ @Deprecated private final File mProcStatsUidstat; private final File mStatsUid; /** Path to {@code /proc/net/dev}. */ private final File mStatsIface; /** Path to {@code /proc/net/xt_qtaguid/stats}. */ private final File mProcStatsNetfilter; private final File mStatsXtUid; /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */ private final File mStatsXtIface; /** {@link #mProcStatsNetfilter} headers. */ /** {@link #mStatsXtUid} headers. */ private static final String KEY_IFACE = "iface"; private static final String KEY_TAG_HEX = "acct_tag_hex"; private static final String KEY_UID = "uid_tag_int"; Loading Loading @@ -137,8 +141,10 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext = context; mObservers = new ArrayList<INetworkManagementEventObserver>(); mProcStatsUidstat = new File(procRoot, "uid_stat"); mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats"); mStatsUid = new File(procRoot, "uid_stat"); mStatsIface = new File(procRoot, "net/dev"); mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat"); if ("simulator".equals(SystemProperties.get("ro.product.device"))) { return; Loading @@ -161,9 +167,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { } // @VisibleForTesting public static NetworkManagementService createForTest(Context context, File procRoot) { public static NetworkManagementService createForTest( Context context, File procRoot, boolean bandwidthControlEnabled) { // TODO: eventually connect with mock netd return new NetworkManagementService(context, procRoot); final NetworkManagementService service = new NetworkManagementService(context, procRoot); service.mBandwidthControlEnabled = bandwidthControlEnabled; return service; } public void systemReady() { Loading Loading @@ -930,13 +939,68 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); final String[] ifaces = listInterfaces(); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); final NetworkStats.Entry entry = new NetworkStats.Entry(); final HashSet<String> activeIfaces = Sets.newHashSet(); final ArrayList<String> values = Lists.newArrayList(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(mStatsIface)); // skip first two header lines reader.readLine(); reader.readLine(); // parse remaining lines String line; while ((line = reader.readLine()) != null) { splitLine(line, values); try { entry.iface = values.get(0); entry.uid = UID_ALL; entry.tag = TAG_NONE; entry.rxBytes = Long.parseLong(values.get(1)); entry.rxPackets = Long.parseLong(values.get(2)); entry.txBytes = Long.parseLong(values.get(9)); entry.txPackets = Long.parseLong(values.get(10)); activeIfaces.add(entry.iface); stats.addValues(entry); } catch (NumberFormatException e) { Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } } catch (IOException e) { Slog.w(TAG, "problem parsing stats: " + e); } finally { IoUtils.closeQuietly(reader); } if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces); // splice in stats from any disabled ifaces if (mBandwidthControlEnabled) { final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface)); xtIfaces.removeAll(activeIfaces); for (String iface : xtIfaces) { final File ifacePath = new File(mStatsXtIface, iface); entry.iface = iface; entry.uid = UID_ALL; entry.tag = TAG_NONE; entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes")); entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets")); entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes")); entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets")); for (String iface : ifaces) { final long rx = getInterfaceCounter(iface, true); final long tx = getInterfaceCounter(iface, false); stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx); stats.addValues(entry); } if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces); } return stats; Loading Loading @@ -1063,13 +1127,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { */ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); final NetworkStats.Entry entry = new NetworkStats.Entry(); final ArrayList<String> keys = Lists.newArrayList(); final ArrayList<String> values = Lists.newArrayList(); final HashMap<String, String> parsed = Maps.newHashMap(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(mProcStatsNetfilter)); reader = new BufferedReader(new FileReader(mStatsXtUid)); // parse first line as header String line = reader.readLine(); Loading @@ -1081,15 +1147,16 @@ class NetworkManagementService extends INetworkManagementService.Stub { parseLine(keys, values, parsed); try { final String iface = parsed.get(KEY_IFACE); final int tag = NetworkManagementSocketTagger.kernelToTag( // TODO: add rxPackets/txPackets once kernel exports entry.iface = parsed.get(KEY_IFACE); entry.tag = NetworkManagementSocketTagger.kernelToTag( parsed.get(KEY_TAG_HEX)); final int uid = Integer.parseInt(parsed.get(KEY_UID)); final long rx = Long.parseLong(parsed.get(KEY_RX)); final long tx = Long.parseLong(parsed.get(KEY_TX)); entry.uid = Integer.parseInt(parsed.get(KEY_UID)); entry.rxBytes = Long.parseLong(parsed.get(KEY_RX)); entry.txBytes = Long.parseLong(parsed.get(KEY_TX)); if (limitUid == UID_ALL || limitUid == uid) { stats.addEntry(iface, uid, tag, rx, tx); if (limitUid == UID_ALL || limitUid == entry.uid) { stats.addValues(entry); } } catch (NumberFormatException e) { Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); Loading @@ -1114,19 +1181,27 @@ class NetworkManagementService extends INetworkManagementService.Stub { private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { final String[] knownUids; if (limitUid == UID_ALL) { knownUids = mProcStatsUidstat.list(); knownUids = fileListWithoutNull(mStatsUid); } else { knownUids = new String[] { String.valueOf(limitUid) }; } final NetworkStats stats = new NetworkStats( SystemClock.elapsedRealtime(), knownUids.length); final NetworkStats.Entry entry = new NetworkStats.Entry(); for (String uid : knownUids) { final int uidInt = Integer.parseInt(uid); final File uidPath = new File(mProcStatsUidstat, uid); final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); final File uidPath = new File(mStatsUid, uid); entry.iface = IFACE_ALL; entry.uid = uidInt; entry.tag = TAG_NONE; entry.rxBytes = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); entry.rxPackets = readSingleLongFromFile(new File(uidPath, "tcp_rcv_pkt")); entry.txBytes = readSingleLongFromFile(new File(uidPath, "tcp_snd")); entry.txPackets = readSingleLongFromFile(new File(uidPath, "tcp_snd_pkt")); stats.addValues(entry); } return stats; Loading Loading @@ -1197,7 +1272,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static void splitLine(String line, ArrayList<String> outSplit) { outSplit.clear(); final StringTokenizer t = new StringTokenizer(line); final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:"); while (t.hasMoreTokens()) { outSplit.add(t.nextToken()); } Loading Loading @@ -1232,6 +1307,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { } } /** * Wrapper for {@link File#list()} that returns empty array instead of * {@code null}. */ private static String[] fileListWithoutNull(File file) { final String[] list = file.list(); return list != null ? list : new String[0]; } public void setDefaultInterfaceForDns(String iface) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); Loading services/tests/servicestests/res/raw/net_dev_typical 0 → 100644 +8 −0 Original line number Diff line number Diff line Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0 rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0 ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0 ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0 services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java +58 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import static com.android.server.NetworkManagementSocketTagger.tagToKernel; Loading @@ -25,9 +27,11 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import com.android.frameworks.servicestests.R; import com.google.common.io.Files; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.InputStream; import java.io.OutputStream; Loading @@ -46,14 +50,23 @@ public class NetworkManagementServiceTest extends AndroidTestCase { public void setUp() throws Exception { super.setUp(); mTestProc = getContext().getFilesDir(); mService = NetworkManagementService.createForTest(mContext, mTestProc); final File canonicalFilesDir = getContext().getFilesDir().getCanonicalFile(); mTestProc = new File(canonicalFilesDir, "proc"); if (mTestProc.exists()) { Files.deleteRecursively(mTestProc); } mService = NetworkManagementService.createForTest(mContext, mTestProc, true); } @Override public void tearDown() throws Exception { mService = null; if (mTestProc.exists()) { Files.deleteRecursively(mTestProc); } super.tearDown(); } Loading @@ -61,7 +74,7 @@ public class NetworkManagementServiceTest extends AndroidTestCase { stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats")); final NetworkStats stats = mService.getNetworkStatsDetail(); assertEquals(31, stats.size); assertEquals(31, stats.size()); assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L); assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L); assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L); Loading @@ -73,11 +86,37 @@ public class NetworkManagementServiceTest extends AndroidTestCase { stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats")); final NetworkStats stats = mService.getNetworkStatsDetail(); assertEquals(2, stats.size); assertEquals(2, stats.size()); assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L); assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L); } public void testNetworkStatsSummary() throws Exception { stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev")); final NetworkStats stats = mService.getNetworkStatsSummary(); assertEquals(6, stats.size()); assertStatsEntry(stats, "lo", UID_ALL, TAG_NONE, 8308L, 8308L); assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L); assertStatsEntry(stats, "ifb0", UID_ALL, TAG_NONE, 52454L, 0L); assertStatsEntry(stats, "ifb1", UID_ALL, TAG_NONE, 52454L, 0L); assertStatsEntry(stats, "sit0", UID_ALL, TAG_NONE, 0L, 0L); assertStatsEntry(stats, "ip6tnl0", UID_ALL, TAG_NONE, 0L, 0L); } public void testNetworkStatsSummaryDown() throws Exception { stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev")); stageLong(1024L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_bytes")); stageLong(128L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_packets")); stageLong(2048L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_bytes")); stageLong(256L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_packets")); final NetworkStats stats = mService.getNetworkStatsSummary(); assertEquals(7, stats.size()); assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L); assertStatsEntry(stats, "wlan0", UID_ALL, TAG_NONE, 1024L, 2048L); } public void testKernelTags() throws Exception { assertEquals("0", tagToKernel(0x0)); assertEquals("214748364800", tagToKernel(0x32)); Loading @@ -90,7 +129,6 @@ public class NetworkManagementServiceTest extends AndroidTestCase { assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); assertEquals(0, kernelToTag("0x0000000000000000")); assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); } /** Loading @@ -111,11 +149,23 @@ public class NetworkManagementServiceTest extends AndroidTestCase { } } private void stageLong(long value, File file) throws Exception { new File(file.getParent()).mkdirs(); FileWriter out = null; try { out = new FileWriter(file); out.write(Long.toString(value)); } finally { IoUtils.closeQuietly(out); } } private static void assertStatsEntry( NetworkStats stats, String iface, int uid, int tag, long rx, long tx) { NetworkStats stats, String iface, int uid, int tag, long rxBytes, long txBytes) { final int i = stats.findIndex(iface, uid, tag); assertEquals(rx, stats.rx[i]); assertEquals(tx, stats.tx[i]); final NetworkStats.Entry entry = stats.getValues(i, null); assertEquals(rxBytes, entry.rxBytes); assertEquals(txBytes, entry.txBytes); } } Loading
core/java/android/net/NetworkStats.java +110 −25 Original line number Diff line number Diff line Loading @@ -43,17 +43,41 @@ public class NetworkStats implements Parcelable { /** {@link #tag} value for without tag. */ public static final int TAG_NONE = 0; // TODO: move public fields to Entry accessors, then undeprecate // TODO: refactor rx/tx to rxBytes/txBytes /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ @Deprecated public final long elapsedRealtime; @Deprecated public int size; @Deprecated public String[] iface; @Deprecated public int[] uid; @Deprecated public int[] tag; @Deprecated public long[] rx; @Deprecated public long[] rxPackets; @Deprecated public long[] tx; @Deprecated public long[] txPackets; public static class Entry { public String iface; public int uid; public int tag; public long rxBytes; public long rxPackets; public long txBytes; public long txPackets; } public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; Loading @@ -62,7 +86,9 @@ public class NetworkStats implements Parcelable { this.uid = new int[initialSize]; this.tag = new int[initialSize]; this.rx = new long[initialSize]; this.rxPackets = new long[initialSize]; this.tx = new long[initialSize]; this.txPackets = new long[initialSize]; } public NetworkStats(Parcel parcel) { Loading @@ -72,38 +98,82 @@ public class NetworkStats implements Parcelable { uid = parcel.createIntArray(); tag = parcel.createIntArray(); rx = parcel.createLongArray(); rxPackets = parcel.createLongArray(); tx = parcel.createLongArray(); txPackets = parcel.createLongArray(); } /** * Add new stats entry with given values. */ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { final Entry entry = new Entry(); entry.iface = iface; entry.uid = uid; entry.tag = tag; entry.rxBytes = rx; entry.txBytes = tx; return addValues(entry); } /** * Add new stats entry, copying from given {@link Entry}. The {@link Entry} * object can be recycled across multiple calls. */ public NetworkStats addValues(Entry entry) { if (size >= this.iface.length) { final int newLength = Math.max(this.iface.length, 10) * 3 / 2; this.iface = Arrays.copyOf(this.iface, newLength); this.uid = Arrays.copyOf(this.uid, newLength); this.tag = Arrays.copyOf(this.tag, newLength); this.rx = Arrays.copyOf(this.rx, newLength); this.tx = Arrays.copyOf(this.tx, newLength); } this.iface[size] = iface; this.uid[size] = uid; this.tag[size] = tag; this.rx[size] = rx; this.tx[size] = tx; final int newLength = Math.max(iface.length, 10) * 3 / 2; iface = Arrays.copyOf(iface, newLength); uid = Arrays.copyOf(uid, newLength); tag = Arrays.copyOf(tag, newLength); rx = Arrays.copyOf(rx, newLength); rxPackets = Arrays.copyOf(rxPackets, newLength); tx = Arrays.copyOf(tx, newLength); txPackets = Arrays.copyOf(txPackets, newLength); } iface[size] = entry.iface; uid[size] = entry.uid; tag[size] = entry.tag; rx[size] = entry.rxBytes; rxPackets[size] = entry.rxPackets; tx[size] = entry.txBytes; txPackets[size] = entry.txPackets; size++; return this; } /** * Return specific stats entry. */ public Entry getValues(int i, Entry recycle) { final Entry entry = recycle != null ? recycle : new Entry(); entry.iface = iface[i]; entry.uid = uid[i]; entry.tag = tag[i]; entry.rxBytes = rx[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txPackets = txPackets[i]; return entry; } public long getElapsedRealtime() { return elapsedRealtime; } public int size() { return size; } /** * Combine given values with an existing row, or create a new row if * {@link #findIndex(String, int, int)} is unable to find match. Can also be * used to subtract values from existing rows. */ public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { // TODO: extent to accept rxPackets/txPackets final int i = findIndex(iface, uid, tag); if (i == -1) { // only create new entry when positive contribution Loading Loading @@ -199,30 +269,41 @@ public class NetworkStats implements Parcelable { } // result will have our rows, and elapsed time between snapshots final Entry entry = new Entry(); final NetworkStats result = new NetworkStats(deltaRealtime, size); for (int i = 0; i < size; i++) { final String iface = this.iface[i]; final int uid = this.uid[i]; final int tag = this.tag[i]; entry.iface = iface[i]; entry.uid = uid[i]; entry.tag = tag[i]; // find remote row that matches, and subtract final int j = value.findIndex(iface, uid, tag); final int j = value.findIndex(entry.iface, entry.uid, entry.tag); if (j == -1) { // newly appearing row, return entire value result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]); entry.rxBytes = rx[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txPackets = txPackets[i]; } else { // existing row, subtract remote value long rx = this.rx[i] - value.rx[j]; long tx = this.tx[i] - value.tx[j]; if (enforceMonotonic && (rx < 0 || tx < 0)) { entry.rxBytes = rx[i] - value.rx[j]; entry.rxPackets = rxPackets[i] - value.rxPackets[j]; entry.txBytes = tx[i] - value.tx[j]; entry.txPackets = txPackets[i] - value.txPackets[j]; if (enforceMonotonic && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0)) { throw new IllegalArgumentException("found non-monotonic values"); } if (clampNegative) { rx = Math.max(0, rx); tx = Math.max(0, tx); entry.rxBytes = Math.max(0, entry.rxBytes); entry.rxPackets = Math.max(0, entry.rxPackets); entry.txBytes = Math.max(0, entry.txBytes); entry.txPackets = Math.max(0, entry.txPackets); } result.addEntry(iface, uid, tag, rx, tx); } result.addValues(entry); } return result; Loading @@ -235,13 +316,15 @@ public class NetworkStats implements Parcelable { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); for (int i = 0; i < iface.length; i++) { for (int i = 0; i < size; i++) { 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(" rx="); pw.print(rx[i]); pw.print(" tx="); pw.println(tx[i]); pw.print(" rxBytes="); pw.print(rx[i]); pw.print(" rxPackets="); pw.print(rxPackets[i]); pw.print(" txBytes="); pw.print(tx[i]); pw.print(" txPackets="); pw.println(txPackets[i]); } } Loading @@ -265,7 +348,9 @@ public class NetworkStats implements Parcelable { dest.writeIntArray(uid); dest.writeIntArray(tag); dest.writeLongArray(rx); dest.writeLongArray(rxPackets); dest.writeLongArray(tx); dest.writeLongArray(txPackets); } public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { Loading
core/tests/coretests/src/android/net/NetworkStatsTest.java +3 −3 Original line number Diff line number Diff line Loading @@ -44,20 +44,20 @@ public class NetworkStatsTest extends TestCase { public void testAddEntryGrow() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 2); assertEquals(0, stats.size); assertEquals(0, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L); assertEquals(2, stats.size); assertEquals(2, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L); assertEquals(5, stats.size); assertEquals(5, stats.size()); assertTrue(stats.iface.length >= 5); assertEquals(1L, stats.rx[0]); Loading
services/java/com/android/server/NetworkManagementService.java +111 −27 Original line number Diff line number Diff line Loading @@ -77,11 +77,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { /** Path to {@code /proc/uid_stat}. */ @Deprecated private final File mProcStatsUidstat; private final File mStatsUid; /** Path to {@code /proc/net/dev}. */ private final File mStatsIface; /** Path to {@code /proc/net/xt_qtaguid/stats}. */ private final File mProcStatsNetfilter; private final File mStatsXtUid; /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */ private final File mStatsXtIface; /** {@link #mProcStatsNetfilter} headers. */ /** {@link #mStatsXtUid} headers. */ private static final String KEY_IFACE = "iface"; private static final String KEY_TAG_HEX = "acct_tag_hex"; private static final String KEY_UID = "uid_tag_int"; Loading Loading @@ -137,8 +141,10 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext = context; mObservers = new ArrayList<INetworkManagementEventObserver>(); mProcStatsUidstat = new File(procRoot, "uid_stat"); mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats"); mStatsUid = new File(procRoot, "uid_stat"); mStatsIface = new File(procRoot, "net/dev"); mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat"); if ("simulator".equals(SystemProperties.get("ro.product.device"))) { return; Loading @@ -161,9 +167,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { } // @VisibleForTesting public static NetworkManagementService createForTest(Context context, File procRoot) { public static NetworkManagementService createForTest( Context context, File procRoot, boolean bandwidthControlEnabled) { // TODO: eventually connect with mock netd return new NetworkManagementService(context, procRoot); final NetworkManagementService service = new NetworkManagementService(context, procRoot); service.mBandwidthControlEnabled = bandwidthControlEnabled; return service; } public void systemReady() { Loading Loading @@ -930,13 +939,68 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); final String[] ifaces = listInterfaces(); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); final NetworkStats.Entry entry = new NetworkStats.Entry(); final HashSet<String> activeIfaces = Sets.newHashSet(); final ArrayList<String> values = Lists.newArrayList(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(mStatsIface)); // skip first two header lines reader.readLine(); reader.readLine(); // parse remaining lines String line; while ((line = reader.readLine()) != null) { splitLine(line, values); try { entry.iface = values.get(0); entry.uid = UID_ALL; entry.tag = TAG_NONE; entry.rxBytes = Long.parseLong(values.get(1)); entry.rxPackets = Long.parseLong(values.get(2)); entry.txBytes = Long.parseLong(values.get(9)); entry.txPackets = Long.parseLong(values.get(10)); activeIfaces.add(entry.iface); stats.addValues(entry); } catch (NumberFormatException e) { Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } } catch (IOException e) { Slog.w(TAG, "problem parsing stats: " + e); } finally { IoUtils.closeQuietly(reader); } if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces); // splice in stats from any disabled ifaces if (mBandwidthControlEnabled) { final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface)); xtIfaces.removeAll(activeIfaces); for (String iface : xtIfaces) { final File ifacePath = new File(mStatsXtIface, iface); entry.iface = iface; entry.uid = UID_ALL; entry.tag = TAG_NONE; entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes")); entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets")); entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes")); entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets")); for (String iface : ifaces) { final long rx = getInterfaceCounter(iface, true); final long tx = getInterfaceCounter(iface, false); stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx); stats.addValues(entry); } if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces); } return stats; Loading Loading @@ -1063,13 +1127,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { */ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); final NetworkStats.Entry entry = new NetworkStats.Entry(); final ArrayList<String> keys = Lists.newArrayList(); final ArrayList<String> values = Lists.newArrayList(); final HashMap<String, String> parsed = Maps.newHashMap(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(mProcStatsNetfilter)); reader = new BufferedReader(new FileReader(mStatsXtUid)); // parse first line as header String line = reader.readLine(); Loading @@ -1081,15 +1147,16 @@ class NetworkManagementService extends INetworkManagementService.Stub { parseLine(keys, values, parsed); try { final String iface = parsed.get(KEY_IFACE); final int tag = NetworkManagementSocketTagger.kernelToTag( // TODO: add rxPackets/txPackets once kernel exports entry.iface = parsed.get(KEY_IFACE); entry.tag = NetworkManagementSocketTagger.kernelToTag( parsed.get(KEY_TAG_HEX)); final int uid = Integer.parseInt(parsed.get(KEY_UID)); final long rx = Long.parseLong(parsed.get(KEY_RX)); final long tx = Long.parseLong(parsed.get(KEY_TX)); entry.uid = Integer.parseInt(parsed.get(KEY_UID)); entry.rxBytes = Long.parseLong(parsed.get(KEY_RX)); entry.txBytes = Long.parseLong(parsed.get(KEY_TX)); if (limitUid == UID_ALL || limitUid == uid) { stats.addEntry(iface, uid, tag, rx, tx); if (limitUid == UID_ALL || limitUid == entry.uid) { stats.addValues(entry); } } catch (NumberFormatException e) { Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); Loading @@ -1114,19 +1181,27 @@ class NetworkManagementService extends INetworkManagementService.Stub { private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { final String[] knownUids; if (limitUid == UID_ALL) { knownUids = mProcStatsUidstat.list(); knownUids = fileListWithoutNull(mStatsUid); } else { knownUids = new String[] { String.valueOf(limitUid) }; } final NetworkStats stats = new NetworkStats( SystemClock.elapsedRealtime(), knownUids.length); final NetworkStats.Entry entry = new NetworkStats.Entry(); for (String uid : knownUids) { final int uidInt = Integer.parseInt(uid); final File uidPath = new File(mProcStatsUidstat, uid); final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); final File uidPath = new File(mStatsUid, uid); entry.iface = IFACE_ALL; entry.uid = uidInt; entry.tag = TAG_NONE; entry.rxBytes = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); entry.rxPackets = readSingleLongFromFile(new File(uidPath, "tcp_rcv_pkt")); entry.txBytes = readSingleLongFromFile(new File(uidPath, "tcp_snd")); entry.txPackets = readSingleLongFromFile(new File(uidPath, "tcp_snd_pkt")); stats.addValues(entry); } return stats; Loading Loading @@ -1197,7 +1272,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static void splitLine(String line, ArrayList<String> outSplit) { outSplit.clear(); final StringTokenizer t = new StringTokenizer(line); final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:"); while (t.hasMoreTokens()) { outSplit.add(t.nextToken()); } Loading Loading @@ -1232,6 +1307,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { } } /** * Wrapper for {@link File#list()} that returns empty array instead of * {@code null}. */ private static String[] fileListWithoutNull(File file) { final String[] list = file.list(); return list != null ? list : new String[0]; } public void setDefaultInterfaceForDns(String iface) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); Loading
services/tests/servicestests/res/raw/net_dev_typical 0 → 100644 +8 −0 Original line number Diff line number Diff line Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0 rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0 ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0 ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0
services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java +58 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import static com.android.server.NetworkManagementSocketTagger.tagToKernel; Loading @@ -25,9 +27,11 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import com.android.frameworks.servicestests.R; import com.google.common.io.Files; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.InputStream; import java.io.OutputStream; Loading @@ -46,14 +50,23 @@ public class NetworkManagementServiceTest extends AndroidTestCase { public void setUp() throws Exception { super.setUp(); mTestProc = getContext().getFilesDir(); mService = NetworkManagementService.createForTest(mContext, mTestProc); final File canonicalFilesDir = getContext().getFilesDir().getCanonicalFile(); mTestProc = new File(canonicalFilesDir, "proc"); if (mTestProc.exists()) { Files.deleteRecursively(mTestProc); } mService = NetworkManagementService.createForTest(mContext, mTestProc, true); } @Override public void tearDown() throws Exception { mService = null; if (mTestProc.exists()) { Files.deleteRecursively(mTestProc); } super.tearDown(); } Loading @@ -61,7 +74,7 @@ public class NetworkManagementServiceTest extends AndroidTestCase { stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats")); final NetworkStats stats = mService.getNetworkStatsDetail(); assertEquals(31, stats.size); assertEquals(31, stats.size()); assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L); assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L); assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L); Loading @@ -73,11 +86,37 @@ public class NetworkManagementServiceTest extends AndroidTestCase { stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats")); final NetworkStats stats = mService.getNetworkStatsDetail(); assertEquals(2, stats.size); assertEquals(2, stats.size()); assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L); assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L); } public void testNetworkStatsSummary() throws Exception { stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev")); final NetworkStats stats = mService.getNetworkStatsSummary(); assertEquals(6, stats.size()); assertStatsEntry(stats, "lo", UID_ALL, TAG_NONE, 8308L, 8308L); assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L); assertStatsEntry(stats, "ifb0", UID_ALL, TAG_NONE, 52454L, 0L); assertStatsEntry(stats, "ifb1", UID_ALL, TAG_NONE, 52454L, 0L); assertStatsEntry(stats, "sit0", UID_ALL, TAG_NONE, 0L, 0L); assertStatsEntry(stats, "ip6tnl0", UID_ALL, TAG_NONE, 0L, 0L); } public void testNetworkStatsSummaryDown() throws Exception { stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev")); stageLong(1024L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_bytes")); stageLong(128L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_packets")); stageLong(2048L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_bytes")); stageLong(256L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_packets")); final NetworkStats stats = mService.getNetworkStatsSummary(); assertEquals(7, stats.size()); assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L); assertStatsEntry(stats, "wlan0", UID_ALL, TAG_NONE, 1024L, 2048L); } public void testKernelTags() throws Exception { assertEquals("0", tagToKernel(0x0)); assertEquals("214748364800", tagToKernel(0x32)); Loading @@ -90,7 +129,6 @@ public class NetworkManagementServiceTest extends AndroidTestCase { assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); assertEquals(0, kernelToTag("0x0000000000000000")); assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); } /** Loading @@ -111,11 +149,23 @@ public class NetworkManagementServiceTest extends AndroidTestCase { } } private void stageLong(long value, File file) throws Exception { new File(file.getParent()).mkdirs(); FileWriter out = null; try { out = new FileWriter(file); out.write(Long.toString(value)); } finally { IoUtils.closeQuietly(out); } } private static void assertStatsEntry( NetworkStats stats, String iface, int uid, int tag, long rx, long tx) { NetworkStats stats, String iface, int uid, int tag, long rxBytes, long txBytes) { final int i = stats.findIndex(iface, uid, tag); assertEquals(rx, stats.rx[i]); assertEquals(tx, stats.tx[i]); final NetworkStats.Entry entry = stats.getValues(i, null); assertEquals(rxBytes, entry.rxBytes); assertEquals(txBytes, entry.txBytes); } }