Merge "Add method to NetworkStatsService for UID stats." into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4cac91f292
@@ -41,6 +41,16 @@ interface INetworkStatsService {
|
|||||||
|
|
||||||
/** Return data layer snapshot of UID network usage. */
|
/** Return data layer snapshot of UID network usage. */
|
||||||
NetworkStats getDataLayerSnapshotForUid(int uid);
|
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. */
|
/** Return set of any ifaces associated with mobile networks since boot. */
|
||||||
String[] getMobileIfaces();
|
String[] getMobileIfaces();
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ public class NetworkStats implements Parcelable {
|
|||||||
/** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
|
/** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
|
||||||
public static final int SET_DBG_VPN_OUT = 1002;
|
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. */
|
/** {@link #tag} value for total data across all tags. */
|
||||||
// TODO: Rename TAG_NONE to TAG_ALL.
|
// TODO: Rename TAG_NONE to TAG_ALL.
|
||||||
public static final int TAG_NONE = 0;
|
public static final int TAG_NONE = 0;
|
||||||
@@ -366,23 +369,27 @@ public class NetworkStats implements Parcelable {
|
|||||||
capacity = newLength;
|
capacity = newLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
iface[size] = entry.iface;
|
setValues(size, entry);
|
||||||
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;
|
|
||||||
size++;
|
size++;
|
||||||
|
|
||||||
return this;
|
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.
|
* Return specific stats entry.
|
||||||
*/
|
*/
|
||||||
@@ -831,6 +838,39 @@ public class NetworkStats implements Parcelable {
|
|||||||
return stats;
|
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) {
|
public void dump(String prefix, PrintWriter pw) {
|
||||||
pw.print(prefix);
|
pw.print(prefix);
|
||||||
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
|
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
|
||||||
|
|||||||
@@ -22,16 +22,14 @@ import static android.net.NetworkStats.TAG_NONE;
|
|||||||
import static android.net.NetworkStats.UID_ALL;
|
import static android.net.NetworkStats.UID_ALL;
|
||||||
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
|
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.ArrayMap;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.ProcFileReader;
|
import com.android.internal.util.ProcFileReader;
|
||||||
import com.google.android.collect.Lists;
|
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
@@ -41,8 +39,10 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ProtocolException;
|
import java.net.ProtocolException;
|
||||||
import java.util.ArrayList;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link NetworkStats} instances by parsing various {@code /proc/}
|
* Creates {@link NetworkStats} instances by parsing various {@code /proc/}
|
||||||
@@ -72,18 +72,55 @@ public class NetworkStatsFactory {
|
|||||||
|
|
||||||
private boolean mUseBpfStats;
|
private boolean mUseBpfStats;
|
||||||
|
|
||||||
// TODO: to improve testability and avoid global state, do not use a static variable.
|
// TODO: only do adjustments in NetworkStatsService and remove this.
|
||||||
@GuardedBy("sStackedIfaces")
|
/**
|
||||||
private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
|
* (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) {
|
public static void noteStackedIface(String stackedIface, String baseIface) {
|
||||||
synchronized (sStackedIfaces) {
|
if (stackedIface != null && baseIface != null) {
|
||||||
if (baseIface != null) {
|
|
||||||
sStackedIfaces.put(stackedIface, baseIface);
|
sStackedIfaces.put(stackedIface, baseIface);
|
||||||
} else {
|
|
||||||
sStackedIfaces.remove(stackedIface);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
public NetworkStatsFactory() {
|
||||||
@@ -252,12 +289,9 @@ public class NetworkStatsFactory {
|
|||||||
NetworkStats lastStats) throws IOException {
|
NetworkStats lastStats) throws IOException {
|
||||||
final NetworkStats stats =
|
final NetworkStats stats =
|
||||||
readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
|
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.
|
// 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
|
NetworkStats.Entry entry = null; // For recycling
|
||||||
|
|
||||||
@@ -271,7 +305,7 @@ public class NetworkStatsFactory {
|
|||||||
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
|
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final String baseIface = stackedIfaces.get(entry.iface);
|
final String baseIface = sStackedIfaces.get(entry.iface);
|
||||||
if (baseIface == null) {
|
if (baseIface == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
|
|||||||
import static android.net.ConnectivityManager.isNetworkTypeMobile;
|
import static android.net.ConnectivityManager.isNetworkTypeMobile;
|
||||||
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
|
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
|
||||||
import static android.net.NetworkStats.IFACE_ALL;
|
import static android.net.NetworkStats.IFACE_ALL;
|
||||||
|
import static android.net.NetworkStats.INTERFACES_ALL;
|
||||||
import static android.net.NetworkStats.METERED_ALL;
|
import static android.net.NetworkStats.METERED_ALL;
|
||||||
import static android.net.NetworkStats.ROAMING_ALL;
|
import static android.net.NetworkStats.ROAMING_ALL;
|
||||||
import static android.net.NetworkStats.SET_ALL;
|
import static android.net.NetworkStats.SET_ALL;
|
||||||
@@ -34,6 +35,7 @@ import static android.net.NetworkStats.SET_DEFAULT;
|
|||||||
import static android.net.NetworkStats.SET_FOREGROUND;
|
import static android.net.NetworkStats.SET_FOREGROUND;
|
||||||
import static android.net.NetworkStats.STATS_PER_IFACE;
|
import static android.net.NetworkStats.STATS_PER_IFACE;
|
||||||
import static android.net.NetworkStats.STATS_PER_UID;
|
import static android.net.NetworkStats.STATS_PER_UID;
|
||||||
|
import static android.net.NetworkStats.TAG_ALL;
|
||||||
import static android.net.NetworkStats.TAG_NONE;
|
import static android.net.NetworkStats.TAG_NONE;
|
||||||
import static android.net.NetworkStats.UID_ALL;
|
import static android.net.NetworkStats.UID_ALL;
|
||||||
import static android.net.NetworkStatsHistory.FIELD_ALL;
|
import static android.net.NetworkStatsHistory.FIELD_ALL;
|
||||||
@@ -127,6 +129,7 @@ import android.util.proto.ProtoOutputStream;
|
|||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.net.NetworkStatsFactory;
|
||||||
import com.android.internal.net.VpnInfo;
|
import com.android.internal.net.VpnInfo;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
@@ -145,6 +148,7 @@ import java.time.ZoneOffset;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect and persist detailed network statistics, and provide this data to
|
* Collect and persist detailed network statistics, and provide this data to
|
||||||
@@ -739,7 +743,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
final long token = Binder.clearCallingIdentity();
|
final long token = Binder.clearCallingIdentity();
|
||||||
final NetworkStats networkLayer;
|
final NetworkStats networkLayer;
|
||||||
try {
|
try {
|
||||||
networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
|
networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid,
|
||||||
|
NetworkStats.INTERFACES_ALL);
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(token);
|
Binder.restoreCallingIdentity(token);
|
||||||
}
|
}
|
||||||
@@ -760,6 +765,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
return dataLayer;
|
return dataLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
|
||||||
|
try {
|
||||||
|
final String[] ifacesToQuery =
|
||||||
|
NetworkStatsFactory.augmentWithStackedInterfacesLocked(requiredIfaces);
|
||||||
|
return getNetworkStatsUidDetail(ifacesToQuery);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.wtf(TAG, "Error compiling UID stats", e);
|
||||||
|
return new NetworkStats(0L, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getMobileIfaces() {
|
public String[] getMobileIfaces() {
|
||||||
return mMobileIfaces;
|
return mMobileIfaces;
|
||||||
@@ -1119,6 +1136,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
mobileIfaces.add(stackedIface);
|
mobileIfaces.add(stackedIface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkStatsFactory.noteStackedIface(stackedIface, baseIface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1141,7 +1160,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
private void recordSnapshotLocked(long currentTime) throws RemoteException {
|
private void recordSnapshotLocked(long currentTime) throws RemoteException {
|
||||||
// snapshot and record current counters; read UID stats first to
|
// snapshot and record current counters; read UID stats first to
|
||||||
// avoid over counting dev stats.
|
// avoid over counting dev stats.
|
||||||
final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
|
final NetworkStats uidSnapshot = getNetworkStatsUidDetail(INTERFACES_ALL);
|
||||||
final NetworkStats xtSnapshot = getNetworkStatsXt();
|
final NetworkStats xtSnapshot = getNetworkStatsXt();
|
||||||
final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
|
final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
|
||||||
|
|
||||||
@@ -1501,12 +1520,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
* Return snapshot of current UID statistics, including any
|
* Return snapshot of current UID statistics, including any
|
||||||
* {@link TrafficStats#UID_TETHERING}, video calling data usage, and {@link #mUidOperations}
|
* {@link TrafficStats#UID_TETHERING}, video calling data usage, and {@link #mUidOperations}
|
||||||
* values.
|
* values.
|
||||||
|
*
|
||||||
|
* @param ifaces A list of interfaces the stats should be restricted to, or
|
||||||
|
* {@link NetworkStats#INTERFACES_ALL}.
|
||||||
*/
|
*/
|
||||||
private NetworkStats getNetworkStatsUidDetail() throws RemoteException {
|
private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
|
||||||
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
|
throws RemoteException {
|
||||||
|
|
||||||
|
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
|
||||||
|
ifaces);
|
||||||
|
|
||||||
// fold tethering stats and operations into uid snapshot
|
// fold tethering stats and operations into uid snapshot
|
||||||
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
|
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
|
||||||
|
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
|
||||||
uidSnapshot.combineAllValues(tetherSnapshot);
|
uidSnapshot.combineAllValues(tetherSnapshot);
|
||||||
|
|
||||||
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
|
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
|
||||||
@@ -1515,10 +1541,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
// fold video calling data usage stats into uid snapshot
|
// fold video calling data usage stats into uid snapshot
|
||||||
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
|
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
|
||||||
if (vtStats != null) {
|
if (vtStats != null) {
|
||||||
|
vtStats.filter(UID_ALL, ifaces, TAG_ALL);
|
||||||
uidSnapshot.combineAllValues(vtStats);
|
uidSnapshot.combineAllValues(vtStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
uidSnapshot.combineAllValues(mUidOperations);
|
uidSnapshot.combineAllValues(mUidOperations);
|
||||||
|
|
||||||
|
// TODO: apply tethering & VC 464xlat adjustments here
|
||||||
|
|
||||||
return uidSnapshot;
|
return uidSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user