Merge "Add method to NetworkStatsService for UID stats." into pi-dev

This commit is contained in:
Remi NGUYEN VAN
2018-04-02 05:40:04 +00:00
committed by Android (Google) Code Review
4 changed files with 149 additions and 35 deletions

View File

@@ -41,6 +41,16 @@ interface INetworkStatsService {
/** Return data layer snapshot of UID network usage. */
NetworkStats getDataLayerSnapshotForUid(int uid);
/** Get a detailed snapshot of stats since boot for all UIDs.
*
* <p>Results will not always be limited to stats on requiredIfaces when specified: stats for
* interfaces stacked on the specified interfaces, or for interfaces on which the specified
* interfaces are stacked on, will also be included.
* @param requiredIfaces Interface names to get data for, or {@link NetworkStats#INTERFACES_ALL}.
*/
NetworkStats getDetailedUidStats(in String[] requiredIfaces);
/** Return set of any ifaces associated with mobile networks since boot. */
String[] getMobileIfaces();

View File

@@ -64,6 +64,9 @@ public class NetworkStats implements Parcelable {
/** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
public static final int SET_DBG_VPN_OUT = 1002;
/** Include all interfaces when filtering */
public static final String[] INTERFACES_ALL = null;
/** {@link #tag} value for total data across all tags. */
// TODO: Rename TAG_NONE to TAG_ALL.
public static final int TAG_NONE = 0;
@@ -366,23 +369,27 @@ public class NetworkStats implements Parcelable {
capacity = newLength;
}
iface[size] = entry.iface;
uid[size] = entry.uid;
set[size] = entry.set;
tag[size] = entry.tag;
metered[size] = entry.metered;
roaming[size] = entry.roaming;
defaultNetwork[size] = entry.defaultNetwork;
rxBytes[size] = entry.rxBytes;
rxPackets[size] = entry.rxPackets;
txBytes[size] = entry.txBytes;
txPackets[size] = entry.txPackets;
operations[size] = entry.operations;
setValues(size, entry);
size++;
return this;
}
private void setValues(int i, Entry entry) {
iface[i] = entry.iface;
uid[i] = entry.uid;
set[i] = entry.set;
tag[i] = entry.tag;
metered[i] = entry.metered;
roaming[i] = entry.roaming;
defaultNetwork[i] = entry.defaultNetwork;
rxBytes[i] = entry.rxBytes;
rxPackets[i] = entry.rxPackets;
txBytes[i] = entry.txBytes;
txPackets[i] = entry.txPackets;
operations[i] = entry.operations;
}
/**
* Return specific stats entry.
*/
@@ -831,6 +838,39 @@ public class NetworkStats implements Parcelable {
return stats;
}
/**
* Only keep entries that match all specified filters.
*
* <p>This mutates the original structure in place. After this method is called,
* size is the number of matching entries, and capacity is the previous capacity.
* @param limitUid UID to filter for, or {@link #UID_ALL}.
* @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
* @param limitTag Tag to filter for, or {@link #TAG_ALL}.
*/
public void filter(int limitUid, String[] limitIfaces, int limitTag) {
if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
return;
}
Entry entry = new Entry();
int nextOutputEntry = 0;
for (int i = 0; i < size; i++) {
entry = getValues(i, entry);
final boolean matches =
(limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)
&& (limitIfaces == INTERFACES_ALL
|| ArrayUtils.contains(limitIfaces, entry.iface));
if (matches) {
setValues(nextOutputEntry, entry);
nextOutputEntry++;
}
}
size = nextOutputEntry;
}
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);

View File

@@ -22,16 +22,14 @@ import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import android.annotation.Nullable;
import android.net.NetworkStats;
import android.os.StrictMode;
import android.os.SystemClock;
import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
import com.google.android.collect.Lists;
import libcore.io.IoUtils;
@@ -41,8 +39,10 @@ import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Creates {@link NetworkStats} instances by parsing various {@code /proc/}
@@ -72,18 +72,55 @@ public class NetworkStatsFactory {
private boolean mUseBpfStats;
// TODO: to improve testability and avoid global state, do not use a static variable.
@GuardedBy("sStackedIfaces")
private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
// TODO: only do adjustments in NetworkStatsService and remove this.
/**
* (Stacked interface) -> (base interface) association for all connected ifaces since boot.
*
* Because counters must never roll backwards, once a given interface is stacked on top of an
* underlying interface, the stacked interface can never be stacked on top of
* another interface. */
private static final ConcurrentHashMap<String, String> sStackedIfaces
= new ConcurrentHashMap<>();
public static void noteStackedIface(String stackedIface, String baseIface) {
synchronized (sStackedIfaces) {
if (baseIface != null) {
sStackedIfaces.put(stackedIface, baseIface);
} else {
sStackedIfaces.remove(stackedIface);
if (stackedIface != null && baseIface != null) {
sStackedIfaces.put(stackedIface, baseIface);
}
}
/**
* Get a set of interfaces containing specified ifaces and stacked interfaces.
*
* <p>The added stacked interfaces are ifaces stacked on top of the specified ones, or ifaces
* on which the specified ones are stacked. Stacked interfaces are those noted with
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
* is called are guaranteed to be included.
*/
public static String[] augmentWithStackedInterfacesLocked(@Nullable String[] requiredIfaces) {
if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
return null;
}
HashSet<String> relatedIfaces = new HashSet<>(Arrays.asList(requiredIfaces));
// ConcurrentHashMap's EntrySet iterators are "guaranteed to traverse
// elements as they existed upon construction exactly once, and may
// (but are not guaranteed to) reflect any modifications subsequent to construction".
// This is enough here.
for (Map.Entry<String, String> entry : sStackedIfaces.entrySet()) {
if (relatedIfaces.contains(entry.getKey())) {
relatedIfaces.add(entry.getValue());
} else if (relatedIfaces.contains(entry.getValue())) {
relatedIfaces.add(entry.getKey());
}
}
String[] outArray = new String[relatedIfaces.size()];
return relatedIfaces.toArray(outArray);
}
@VisibleForTesting
public static void clearStackedIfaces() {
sStackedIfaces.clear();
}
public NetworkStatsFactory() {
@@ -252,12 +289,9 @@ public class NetworkStatsFactory {
NetworkStats lastStats) throws IOException {
final NetworkStats stats =
readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
final ArrayMap<String, String> stackedIfaces;
synchronized (sStackedIfaces) {
stackedIfaces = new ArrayMap<>(sStackedIfaces);
}
// Total 464xlat traffic to subtract from uid 0 on all base interfaces.
final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
// sStackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
final NetworkStats adjustments = new NetworkStats(0, sStackedIfaces.size());
NetworkStats.Entry entry = null; // For recycling
@@ -271,7 +305,7 @@ public class NetworkStatsFactory {
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
continue;
}
final String baseIface = stackedIfaces.get(entry.iface);
final String baseIface = sStackedIfaces.get(entry.iface);
if (baseIface == null) {
continue;
}