Merge "Add method to NetworkStatsService for UID stats."

This commit is contained in:
Remi NGUYEN VAN
2018-03-23 02:39:00 +00:00
committed by Gerrit Code Review
10 changed files with 383 additions and 43 deletions

View File

@@ -44,6 +44,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();

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. */ /** 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;
@@ -359,23 +362,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.
*/ */
@@ -824,6 +831,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);

View File

@@ -268,10 +268,12 @@ interface INetworkManagementService
NetworkStats getNetworkStatsDetail(); NetworkStats getNetworkStatsDetail();
/** /**
* Return detailed network statistics for the requested UID, * Return detailed network statistics for the requested UID and interfaces,
* including interface and tag details. * including interface and tag details.
* @param uid UID to obtain statistics for, or {@link NetworkStats#UID_ALL}.
* @param ifaces Interfaces to obtain statistics for, or {@link NetworkStats#INTERFACES_ALL}.
*/ */
NetworkStats getNetworkStatsUidDetail(int uid); NetworkStats getNetworkStatsUidDetail(int uid, in String[] ifaces);
/** /**
* Return summary of network statistics all tethering interfaces. * Return summary of network statistics all tethering interfaces.

View File

@@ -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/}
@@ -70,18 +70,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() {
@@ -250,12 +287,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
@@ -269,7 +303,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;
} }

View File

@@ -5268,7 +5268,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (LinkProperties stacked : newNetwork.linkProperties.getStackedLinks()) { for (LinkProperties stacked : newNetwork.linkProperties.getStackedLinks()) {
final String stackedIface = stacked.getInterfaceName(); final String stackedIface = stacked.getInterfaceName();
bs.noteNetworkInterfaceType(stackedIface, type); bs.noteNetworkInterfaceType(stackedIface, type);
NetworkStatsFactory.noteStackedIface(stackedIface, baseIface);
} }
} catch (RemoteException ignored) { } catch (RemoteException ignored) {
} }

View File

@@ -1867,10 +1867,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
} }
@Override @Override
public NetworkStats getNetworkStatsUidDetail(int uid) { public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try { try {
return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null); return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL, null);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }

View File

@@ -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;
@@ -128,6 +130,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;
@@ -143,6 +146,7 @@ import java.io.PrintWriter;
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
@@ -726,7 +730,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);
} }
@@ -747,6 +752,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;
@@ -1109,6 +1126,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
if (isMobile) { if (isMobile) {
mobileIfaces.add(stackedIface); mobileIfaces.add(stackedIface);
} }
NetworkStatsFactory.noteStackedIface(stackedIface, baseIface);
} }
} }
} }
@@ -1130,7 +1149,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();
@@ -1464,12 +1483,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(
@@ -1478,10 +1504,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;
} }

View File

@@ -19,6 +19,7 @@ package android.net;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
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.METERED_NO; import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkStats.METERED_YES;
@@ -31,6 +32,7 @@ import static android.net.NetworkStats.SET_DBG_VPN_IN;
import static android.net.NetworkStats.SET_DBG_VPN_OUT; import static android.net.NetworkStats.SET_DBG_VPN_OUT;
import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.IFACE_ALL;
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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -641,6 +643,136 @@ public class NetworkStatsTest {
ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0); ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0);
} }
@Test
public void testFilter_NoFilter() {
NetworkStats.Entry entry1 = new NetworkStats.Entry(
"test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry2 = new NetworkStats.Entry(
"test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry3 = new NetworkStats.Entry(
"test2", 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
.addValues(entry1)
.addValues(entry2)
.addValues(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
assertEquals(3, stats.size());
assertEquals(entry1, stats.getValues(0, null));
assertEquals(entry2, stats.getValues(1, null));
assertEquals(entry3, stats.getValues(2, null));
}
@Test
public void testFilter_UidFilter() {
final int testUid = 10101;
NetworkStats.Entry entry1 = new NetworkStats.Entry(
"test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry2 = new NetworkStats.Entry(
"test2", testUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry3 = new NetworkStats.Entry(
"test2", testUid, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
.addValues(entry1)
.addValues(entry2)
.addValues(entry3);
stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
assertEquals(2, stats.size());
assertEquals(entry2, stats.getValues(0, null));
assertEquals(entry3, stats.getValues(1, null));
}
@Test
public void testFilter_InterfaceFilter() {
final String testIf1 = "testif1";
final String testIf2 = "testif2";
NetworkStats.Entry entry1 = new NetworkStats.Entry(
testIf1, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry2 = new NetworkStats.Entry(
"otherif", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry3 = new NetworkStats.Entry(
testIf1, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry4 = new NetworkStats.Entry(
testIf2, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
.addValues(entry1)
.addValues(entry2)
.addValues(entry3)
.addValues(entry4);
stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
assertEquals(3, stats.size());
assertEquals(entry1, stats.getValues(0, null));
assertEquals(entry3, stats.getValues(1, null));
assertEquals(entry4, stats.getValues(2, null));
}
@Test
public void testFilter_EmptyInterfaceFilter() {
NetworkStats.Entry entry1 = new NetworkStats.Entry(
"if1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry2 = new NetworkStats.Entry(
"if2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
.addValues(entry1)
.addValues(entry2);
stats.filter(UID_ALL, new String[] { }, TAG_ALL);
assertEquals(0, stats.size());
}
@Test
public void testFilter_TagFilter() {
final int testTag = 123;
final int otherTag = 456;
NetworkStats.Entry entry1 = new NetworkStats.Entry(
"test1", 10100, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry2 = new NetworkStats.Entry(
"test2", 10101, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry3 = new NetworkStats.Entry(
"test2", 10101, SET_DEFAULT, otherTag, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
.addValues(entry1)
.addValues(entry2)
.addValues(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, testTag);
assertEquals(2, stats.size());
assertEquals(entry1, stats.getValues(0, null));
assertEquals(entry2, stats.getValues(1, null));
}
private static void assertContains(NetworkStats stats, String iface, int uid, int set, private static void assertContains(NetworkStats stats, String iface, int uid, int set,
int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) { long txBytes, long txPackets, long operations) {

View File

@@ -184,7 +184,7 @@ public class NetworkStatsFactoryTest {
assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L); assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L);
assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L); assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
NetworkStatsFactory.noteStackedIface("v4-wlan0", null); NetworkStatsFactory.clearStackedIfaces();
} }
@Test @Test
@@ -212,7 +212,7 @@ public class NetworkStatsFactoryTest {
assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L); assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L); assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L);
NetworkStatsFactory.noteStackedIface("v4-wlan0", null); NetworkStatsFactory.clearStackedIfaces();
} }
/** /**

View File

@@ -25,6 +25,7 @@ import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
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.METERED_NO; import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkStats.METERED_YES;
@@ -58,6 +59,9 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -95,6 +99,7 @@ import android.util.Log;
import android.util.TrustedTime; import android.util.TrustedTime;
import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -667,6 +672,94 @@ public class NetworkStatsServiceTest {
DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0); DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0);
} }
@Test
public void testDetailedUidStats() throws Exception {
// pretend that network comes online
expectDefaultSettings();
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI);
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
NetworkStats.Entry entry2 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 50L, 5L, 50L, 5L, 0L);
NetworkStats.Entry entry3 = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, 1024L, 8L, 512L, 4L, 0L);
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
.addValues(entry1)
.addValues(entry2)
.addValues(entry3));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
assertEquals(3, stats.size());
entry1.operations = 1;
assertEquals(entry1, stats.getValues(0, null));
entry2.operations = 1;
assertEquals(entry2, stats.getValues(1, null));
assertEquals(entry3, stats.getValues(2, null));
}
@Test
public void testDetailedUidStats_Filtered() throws Exception {
// pretend that network comes online
expectDefaultSettings();
final String stackedIface = "stacked-test0";
final LinkProperties stackedProp = new LinkProperties();
stackedProp.setInterfaceName(stackedIface);
final NetworkState wifiState = buildWifiState();
wifiState.linkProperties.addStackedLink(stackedProp);
expectNetworkState(wifiState);
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI);
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
// Stacked on matching interface
NetworkStats.Entry tetheredStats1 = new NetworkStats.Entry(
stackedIface, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
// Different interface
NetworkStats.Entry tetheredStats2 = new NetworkStats.Entry(
"otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
final String[] ifaceFilter = new String[] { TEST_IFACE };
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
when(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL), any()))
.thenReturn(new NetworkStats(getElapsedRealtime(), 1)
.addValues(uidStats));
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
.thenReturn(new NetworkStats(getElapsedRealtime(), 2)
.addValues(tetheredStats1)
.addValues(tetheredStats2));
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces ->
ifaces != null && ifaces.length == 2
&& ArrayUtils.contains(ifaces, TEST_IFACE)
&& ArrayUtils.contains(ifaces, stackedIface)));
assertEquals(2, stats.size());
assertEquals(uidStats, stats.getValues(0, null));
assertEquals(tetheredStats1, stats.getValues(1, null));
}
@Test @Test
public void testForegroundBackground() throws Exception { public void testForegroundBackground() throws Exception {
// pretend that network comes online // pretend that network comes online
@@ -1056,7 +1149,7 @@ public class NetworkStatsServiceTest {
private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats) private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
throws Exception { throws Exception {
when(mNetManager.getNetworkStatsUidDetail(UID_ALL)).thenReturn(detail); when(mNetManager.getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL)).thenReturn(detail);
// also include tethering details, since they are folded into UID // also include tethering details, since they are folded into UID
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats); when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);