Merge changes Ieb8645ac,I6466ec14,I87deb82b,I995b108e,Ib6521459

am: f4c10e8bc2

Change-Id: I7e95ac0a027295ca1377d00db9d3aa009e1bab10
This commit is contained in:
Varun Anand
2019-06-17 10:22:09 -07:00
committed by android-build-merger
26 changed files with 961 additions and 524 deletions

View File

@@ -66,7 +66,6 @@ interface INetworkStatsService {
/** Force update of ifaces. */ /** Force update of ifaces. */
void forceUpdateIfaces( void forceUpdateIfaces(
in Network[] defaultNetworks, in Network[] defaultNetworks,
in VpnInfo[] vpnArray,
in NetworkState[] networkStates, in NetworkState[] networkStates,
in String activeIface); in String activeIface);
/** Force update of statistics. */ /** Force update of statistics. */

View File

@@ -18,11 +18,11 @@ package android.net;
import static android.os.Process.CLAT_UID; import static android.os.Process.CLAT_UID;
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage; import android.annotation.UnsupportedAppUsage;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -36,6 +36,7 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Predicate;
/** /**
* Collection of active network statistics. Can contain summary details across * Collection of active network statistics. Can contain summary details across
@@ -993,23 +994,33 @@ public class NetworkStats implements Parcelable {
if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) { if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
return; return;
} }
filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
&& (limitTag == TAG_ALL || limitTag == e.tag)
&& (limitIfaces == INTERFACES_ALL
|| ArrayUtils.contains(limitIfaces, e.iface)));
}
/**
* Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}.
*
* <p>This mutates the original structure in place.
*/
public void filterDebugEntries() {
filter(e -> e.set < SET_DEBUG_START);
}
private void filter(Predicate<Entry> predicate) {
Entry entry = new Entry(); Entry entry = new Entry();
int nextOutputEntry = 0; int nextOutputEntry = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
entry = getValues(i, entry); entry = getValues(i, entry);
final boolean matches = if (predicate.test(entry)) {
(limitUid == UID_ALL || limitUid == entry.uid) if (nextOutputEntry != i) {
&& (limitTag == TAG_ALL || limitTag == entry.tag) setValues(nextOutputEntry, entry);
&& (limitIfaces == INTERFACES_ALL }
|| ArrayUtils.contains(limitIfaces, entry.iface));
if (matches) {
setValues(nextOutputEntry, entry);
nextOutputEntry++; nextOutputEntry++;
} }
} }
size = nextOutputEntry; size = nextOutputEntry;
} }
@@ -1175,133 +1186,221 @@ public class NetworkStats implements Parcelable {
/** /**
* VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface. * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
* *
* This method should only be called on delta NetworkStats. Do not call this method on a * <p>This method should only be called on delta NetworkStats. Do not call this method on a
* snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may change
* change over time. * over time.
* *
* This method performs adjustments for one active VPN package and one VPN iface at a time. * <p>This method performs adjustments for one active VPN package and one VPN iface at a time.
*
* It is possible for the VPN software to use multiple underlying networks. This method
* only migrates traffic for the primary underlying network.
* *
* @param tunUid uid of the VPN application * @param tunUid uid of the VPN application
* @param tunIface iface of the vpn tunnel * @param tunIface iface of the vpn tunnel
* @param underlyingIface the primary underlying network iface used by the VPN application * @param underlyingIfaces underlying network ifaces used by the VPN application
* @return true if it successfully adjusts the accounting for VPN, false otherwise
*/ */
public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) { public void migrateTun(int tunUid, @NonNull String tunIface,
Entry tunIfaceTotal = new Entry(); @NonNull String[] underlyingIfaces) {
Entry underlyingIfaceTotal = new Entry(); // Combined usage by all apps using VPN.
final Entry tunIfaceTotal = new Entry();
// Usage by VPN, grouped by its {@code underlyingIfaces}.
final Entry[] perInterfaceTotal = new Entry[underlyingIfaces.length];
// Usage by VPN, summed across all its {@code underlyingIfaces}.
final Entry underlyingIfacesTotal = new Entry();
tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal); for (int i = 0; i < perInterfaceTotal.length; i++) {
perInterfaceTotal[i] = new Entry();
}
// If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app. tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal,
// If tunIface > underlyingIface, the VPN app doesn't get credit for data compression. underlyingIfacesTotal);
// If tunIface < underlyingIfacesTotal, it leaves the overhead traffic in the VPN app.
// If tunIface > underlyingIfacesTotal, the VPN app doesn't get credit for data compression.
// Negative stats should be avoided. // Negative stats should be avoided.
Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal); final Entry[] moved =
if (pool.isEmpty()) { addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal,
return true; perInterfaceTotal, underlyingIfacesTotal);
} deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved);
Entry moved =
addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
if (!moved.isEmpty()) {
Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved="
+ moved);
return false;
}
return true;
} }
/** /**
* Initializes the data used by the migrateTun() method. * Initializes the data used by the migrateTun() method.
* *
* This is the first pass iteration which does the following work: * <p>This is the first pass iteration which does the following work:
* (1) Adds up all the traffic through the tunUid's underlyingIface *
* (both foreground and background). * <ul>
* (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself. * <li>Adds up all the traffic through the tunUid's underlyingIfaces (both foreground and
* background).
* <li>Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
* </ul>
*
* @param tunUid uid of the VPN application
* @param tunIface iface of the vpn tunnel
* @param underlyingIfaces underlying network ifaces used by the VPN application
* @param tunIfaceTotal output parameter; combined data usage by all apps using VPN
* @param perInterfaceTotal output parameter; data usage by VPN app, grouped by its {@code
* underlyingIfaces}
* @param underlyingIfacesTotal output parameter; data usage by VPN, summed across all of its
* {@code underlyingIfaces}
*/ */
private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface, private void tunAdjustmentInit(int tunUid, @NonNull String tunIface,
Entry tunIfaceTotal, Entry underlyingIfaceTotal) { @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal,
Entry recycle = new Entry(); @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
final Entry recycle = new Entry();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
getValues(i, recycle); getValues(i, recycle);
if (recycle.uid == UID_ALL) { if (recycle.uid == UID_ALL) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot adjust VPN accounting on an iface aggregated NetworkStats."); "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
} if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) { }
if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*"); "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
} }
if (recycle.tag != TAG_NONE) {
if (recycle.uid == tunUid && recycle.tag == TAG_NONE // TODO(b/123666283): Take all tags for tunUid into account.
&& Objects.equals(underlyingIface, recycle.iface)) { continue;
underlyingIfaceTotal.add(recycle);
} }
if (recycle.uid != tunUid && recycle.tag == TAG_NONE if (recycle.uid == tunUid) {
&& Objects.equals(tunIface, recycle.iface)) { // Add up traffic through tunUid's underlying interfaces.
for (int j = 0; j < underlyingIfaces.length; j++) {
if (Objects.equals(underlyingIfaces[j], recycle.iface)) {
perInterfaceTotal[j].add(recycle);
underlyingIfacesTotal.add(recycle);
break;
}
}
} else if (tunIface.equals(recycle.iface)) {
// Add up all tunIface traffic excluding traffic from the vpn app itself. // Add up all tunIface traffic excluding traffic from the vpn app itself.
tunIfaceTotal.add(recycle); tunIfaceTotal.add(recycle);
} }
} }
} }
private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) { /**
Entry pool = new Entry(); * Distributes traffic across apps that are using given {@code tunIface}, and returns the total
pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes); * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}.
pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets); *
pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes); * @param tunUid uid of the VPN application
pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets); * @param tunIface iface of the vpn tunnel
pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations); * @param underlyingIfaces underlying network ifaces used by the VPN application
return pool; * @param tunIfaceTotal combined data usage across all apps using {@code tunIface}
} * @param perInterfaceTotal data usage by VPN app, grouped by its {@code underlyingIfaces}
* @param underlyingIfacesTotal data usage by VPN, summed across all of its {@code
* underlyingIfaces}
*/
private Entry[] addTrafficToApplications(int tunUid, @NonNull String tunIface,
@NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal,
@NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
// Traffic that should be moved off of each underlying interface for tunUid (see
// deductTrafficFromVpnApp below).
final Entry[] moved = new Entry[underlyingIfaces.length];
for (int i = 0; i < underlyingIfaces.length; i++) {
moved[i] = new Entry();
}
private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface, final Entry tmpEntry = new Entry();
Entry tunIfaceTotal, Entry pool) { final int origSize = size;
Entry moved = new Entry(); for (int i = 0; i < origSize; i++) {
Entry tmpEntry = new Entry(); if (!Objects.equals(iface[i], tunIface)) {
tmpEntry.iface = underlyingIface; // Consider only entries that go onto the VPN interface.
for (int i = 0; i < size; i++) { continue;
// the vpn app is excluded from the redistribution but all moved traffic will be }
// deducted from the vpn app (see deductTrafficFromVpnApp below). if (uid[i] == tunUid) {
if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) { // Exclude VPN app from the redistribution, as it can choose to create packet
if (tunIfaceTotal.rxBytes > 0) { // streams by writing to itself.
tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; continue;
} else { }
tmpEntry.rxBytes = 0; tmpEntry.uid = uid[i];
} tmpEntry.tag = tag[i];
if (tunIfaceTotal.rxPackets > 0) { tmpEntry.metered = metered[i];
tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; tmpEntry.roaming = roaming[i];
} else { tmpEntry.defaultNetwork = defaultNetwork[i];
tmpEntry.rxPackets = 0;
} // In a first pass, compute this entry's total share of data across all
if (tunIfaceTotal.txBytes > 0) { // underlyingIfaces. This is computed on the basis of the share of this entry's usage
tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes; // over tunIface.
} else { // TODO: Consider refactoring first pass into a separate helper method.
tmpEntry.txBytes = 0; long totalRxBytes = 0;
} if (tunIfaceTotal.rxBytes > 0) {
if (tunIfaceTotal.txPackets > 0) { // Note - The multiplication below should not overflow since NetworkStatsService
tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets; // processes this every time device has transmitted/received amount equivalent to
} else { // global threshold alert (~ 2MB) across all interfaces.
tmpEntry.txPackets = 0; final long rxBytesAcrossUnderlyingIfaces =
} underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
if (tunIfaceTotal.operations > 0) { // app must not be blamed for more than it consumed on tunIface
tmpEntry.operations = totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces);
pool.operations * operations[i] / tunIfaceTotal.operations; }
} else { long totalRxPackets = 0;
tmpEntry.operations = 0; if (tunIfaceTotal.rxPackets > 0) {
} final long rxPacketsAcrossUnderlyingIfaces =
tmpEntry.uid = uid[i]; underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
tmpEntry.tag = tag[i]; totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces);
}
long totalTxBytes = 0;
if (tunIfaceTotal.txBytes > 0) {
final long txBytesAcrossUnderlyingIfaces =
underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces);
}
long totalTxPackets = 0;
if (tunIfaceTotal.txPackets > 0) {
final long txPacketsAcrossUnderlyingIfaces =
underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces);
}
long totalOperations = 0;
if (tunIfaceTotal.operations > 0) {
final long operationsAcrossUnderlyingIfaces =
underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations;
totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces);
}
// In a second pass, distribute these values across interfaces in the proportion that
// each interface represents of the total traffic of the underlying interfaces.
for (int j = 0; j < underlyingIfaces.length; j++) {
tmpEntry.iface = underlyingIfaces[j];
tmpEntry.rxBytes = 0;
// Reset 'set' to correct value since it gets updated when adding debug info below.
tmpEntry.set = set[i]; tmpEntry.set = set[i];
tmpEntry.metered = metered[i]; if (underlyingIfacesTotal.rxBytes > 0) {
tmpEntry.roaming = roaming[i]; tmpEntry.rxBytes =
tmpEntry.defaultNetwork = defaultNetwork[i]; totalRxBytes
* perInterfaceTotal[j].rxBytes
/ underlyingIfacesTotal.rxBytes;
}
tmpEntry.rxPackets = 0;
if (underlyingIfacesTotal.rxPackets > 0) {
tmpEntry.rxPackets =
totalRxPackets
* perInterfaceTotal[j].rxPackets
/ underlyingIfacesTotal.rxPackets;
}
tmpEntry.txBytes = 0;
if (underlyingIfacesTotal.txBytes > 0) {
tmpEntry.txBytes =
totalTxBytes
* perInterfaceTotal[j].txBytes
/ underlyingIfacesTotal.txBytes;
}
tmpEntry.txPackets = 0;
if (underlyingIfacesTotal.txPackets > 0) {
tmpEntry.txPackets =
totalTxPackets
* perInterfaceTotal[j].txPackets
/ underlyingIfacesTotal.txPackets;
}
tmpEntry.operations = 0;
if (underlyingIfacesTotal.operations > 0) {
tmpEntry.operations =
totalOperations
* perInterfaceTotal[j].operations
/ underlyingIfacesTotal.operations;
}
// tmpEntry now contains the migrated data of the i-th entry for the j-th underlying
// interface. Add that data usage to this object.
combineValues(tmpEntry); combineValues(tmpEntry);
if (tag[i] == TAG_NONE) { if (tag[i] == TAG_NONE) {
moved.add(tmpEntry); // Add the migrated data to moved so it is deducted from the VPN app later.
moved[j].add(tmpEntry);
// Add debug info // Add debug info
tmpEntry.set = SET_DBG_VPN_IN; tmpEntry.set = SET_DBG_VPN_IN;
combineValues(tmpEntry); combineValues(tmpEntry);
@@ -1311,38 +1410,45 @@ public class NetworkStats implements Parcelable {
return moved; return moved;
} }
private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) { private void deductTrafficFromVpnApp(
// Add debug info int tunUid,
moved.uid = tunUid; @NonNull String[] underlyingIfaces,
moved.set = SET_DBG_VPN_OUT; @NonNull Entry[] moved) {
moved.tag = TAG_NONE; for (int i = 0; i < underlyingIfaces.length; i++) {
moved.iface = underlyingIface; moved[i].uid = tunUid;
moved.metered = METERED_ALL; // Add debug info
moved.roaming = ROAMING_ALL; moved[i].set = SET_DBG_VPN_OUT;
moved.defaultNetwork = DEFAULT_NETWORK_ALL; moved[i].tag = TAG_NONE;
combineValues(moved); moved[i].iface = underlyingIfaces[i];
moved[i].metered = METERED_ALL;
moved[i].roaming = ROAMING_ALL;
moved[i].defaultNetwork = DEFAULT_NETWORK_ALL;
combineValues(moved[i]);
// Caveat: if the vpn software uses tag, the total tagged traffic may be greater than // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
// the TAG_NONE traffic. // the TAG_NONE traffic.
// //
// Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO, // Relies on the fact that the underlying traffic only has state ROAMING_NO and
// which should be the case as it comes directly from the /proc file. We only blend in the // METERED_NO, which should be the case as it comes directly from the /proc file.
// roaming data after applying these adjustments, by checking the NetworkIdentity of the // We only blend in the roaming data after applying these adjustments, by checking the
// underlying iface. // NetworkIdentity of the underlying iface.
int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, final int idxVpnBackground = findIndex(underlyingIfaces[i], tunUid, SET_DEFAULT,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
if (idxVpnBackground != -1) { if (idxVpnBackground != -1) {
tunSubtract(idxVpnBackground, this, moved); // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed
} // from foreground usage.
tunSubtract(idxVpnBackground, this, moved[i]);
}
int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, final int idxVpnForeground = findIndex(underlyingIfaces[i], tunUid, SET_FOREGROUND,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
if (idxVpnForeground != -1) { if (idxVpnForeground != -1) {
tunSubtract(idxVpnForeground, this, moved); tunSubtract(idxVpnForeground, this, moved[i]);
}
} }
} }
private static void tunSubtract(int i, NetworkStats left, Entry right) { private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) {
long rxBytes = Math.min(left.rxBytes[i], right.rxBytes); long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
left.rxBytes[i] -= rxBytes; left.rxBytes[i] -= rxBytes;
right.rxBytes -= rxBytes; right.rxBytes -= rxBytes;

View File

@@ -19,6 +19,8 @@ package com.android.internal.net;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import java.util.Arrays;
/** /**
* A lightweight container used to carry information of the ongoing VPN. * A lightweight container used to carry information of the ongoing VPN.
* Internal use only.. * Internal use only..
@@ -28,14 +30,14 @@ import android.os.Parcelable;
public class VpnInfo implements Parcelable { public class VpnInfo implements Parcelable {
public int ownerUid; public int ownerUid;
public String vpnIface; public String vpnIface;
public String primaryUnderlyingIface; public String[] underlyingIfaces;
@Override @Override
public String toString() { public String toString() {
return "VpnInfo{" return "VpnInfo{"
+ "ownerUid=" + ownerUid + "ownerUid=" + ownerUid
+ ", vpnIface='" + vpnIface + '\'' + ", vpnIface='" + vpnIface + '\''
+ ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\'' + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\''
+ '}'; + '}';
} }
@@ -48,7 +50,7 @@ public class VpnInfo implements Parcelable {
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ownerUid); dest.writeInt(ownerUid);
dest.writeString(vpnIface); dest.writeString(vpnIface);
dest.writeString(primaryUnderlyingIface); dest.writeStringArray(underlyingIfaces);
} }
public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() { public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
@@ -57,7 +59,7 @@ public class VpnInfo implements Parcelable {
VpnInfo info = new VpnInfo(); VpnInfo info = new VpnInfo();
info.ownerUid = source.readInt(); info.ownerUid = source.readInt();
info.vpnIface = source.readString(); info.vpnIface = source.readString();
info.primaryUnderlyingIface = source.readString(); info.underlyingIfaces = source.readStringArray();
return info; return info;
} }

View File

@@ -19,13 +19,22 @@ package android.net;
import com.google.caliper.BeforeExperiment; import com.google.caliper.BeforeExperiment;
import com.google.caliper.Param; import com.google.caliper.Param;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NetworkStatsBenchmark { public class NetworkStatsBenchmark {
private static final String UNDERLYING_IFACE = "wlan0"; private static final String[] UNDERLYING_IFACES = {"wlan0", "rmnet0"};
private static final String TUN_IFACE = "tun0"; private static final String TUN_IFACE = "tun0";
private static final int TUN_UID = 999999999; private static final int TUN_UID = 999999999;
@Param({"100", "1000"}) @Param({"100", "1000"})
private int mSize; private int mSize;
/**
* Should not be more than the length of {@link #UNDERLYING_IFACES}.
*/
@Param({"1", "2"})
private int mNumUnderlyingIfaces;
private NetworkStats mNetworkStats; private NetworkStats mNetworkStats;
@BeforeExperiment @BeforeExperiment
@@ -33,8 +42,10 @@ public class NetworkStatsBenchmark {
mNetworkStats = new NetworkStats(0, mSize + 2); mNetworkStats = new NetworkStats(0, mSize + 2);
int uid = 0; int uid = 0;
NetworkStats.Entry recycle = new NetworkStats.Entry(); NetworkStats.Entry recycle = new NetworkStats.Entry();
final List<String> allIfaces = getAllIfacesForBenchmark(); // also contains TUN_IFACE.
final int totalIfaces = allIfaces.size();
for (int i = 0; i < mSize; i++) { for (int i = 0; i < mSize; i++) {
recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE; recycle.iface = allIfaces.get(i % totalIfaces);
recycle.uid = uid; recycle.uid = uid;
recycle.set = i % 2; recycle.set = i % 2;
recycle.tag = NetworkStats.TAG_NONE; recycle.tag = NetworkStats.TAG_NONE;
@@ -48,22 +59,39 @@ public class NetworkStatsBenchmark {
uid++; uid++;
} }
} }
recycle.iface = UNDERLYING_IFACE;
recycle.uid = TUN_UID; for (int i = 0; i < mNumUnderlyingIfaces; i++) {
recycle.set = NetworkStats.SET_FOREGROUND; recycle.iface = UNDERLYING_IFACES[i];
recycle.tag = NetworkStats.TAG_NONE; recycle.uid = TUN_UID;
recycle.rxBytes = 90000 * mSize; recycle.set = NetworkStats.SET_FOREGROUND;
recycle.rxPackets = 40 * mSize; recycle.tag = NetworkStats.TAG_NONE;
recycle.txBytes = 180000 * mSize; recycle.rxBytes = 90000 * mSize;
recycle.txPackets = 1200 * mSize; recycle.rxPackets = 40 * mSize;
recycle.operations = 0; recycle.txBytes = 180000 * mSize;
mNetworkStats.addValues(recycle); recycle.txPackets = 1200 * mSize;
recycle.operations = 0;
mNetworkStats.addValues(recycle);
}
}
private String[] getVpnUnderlyingIfaces() {
return Arrays.copyOf(UNDERLYING_IFACES, mNumUnderlyingIfaces);
}
/**
* Same as {@link #getVpnUnderlyingIfaces}, but also contains {@link #TUN_IFACE}.
*/
private List<String> getAllIfacesForBenchmark() {
List<String> ifaces = new ArrayList<>();
ifaces.add(TUN_IFACE);
ifaces.addAll(Arrays.asList(getVpnUnderlyingIfaces()));
return ifaces;
} }
public void timeMigrateTun(int reps) { public void timeMigrateTun(int reps) {
for (int i = 0; i < reps; i++) { for (int i = 0; i < reps; i++) {
NetworkStats stats = mNetworkStats.clone(); NetworkStats stats = mNetworkStats.clone();
stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE); stats.migrateTun(TUN_UID, TUN_IFACE, getVpnUnderlyingIfaces());
} }
} }

View File

@@ -192,6 +192,7 @@ import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver; import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker; import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.net.NetworkStatsFactory;
import com.android.server.utils.PriorityDump; import com.android.server.utils.PriorityDump;
import com.google.android.collect.Lists; import com.google.android.collect.Lists;
@@ -4374,7 +4375,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
/** /**
* @return VPN information for accounting, or null if we can't retrieve all required * @return VPN information for accounting, or null if we can't retrieve all required
* information, e.g primary underlying iface. * information, e.g underlying ifaces.
*/ */
@Nullable @Nullable
private VpnInfo createVpnInfo(Vpn vpn) { private VpnInfo createVpnInfo(Vpn vpn) {
@@ -4386,17 +4387,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list. // the underlyingNetworks list.
if (underlyingNetworks == null) { if (underlyingNetworks == null) {
NetworkAgentInfo defaultNetwork = getDefaultNetwork(); NetworkAgentInfo defaultNai = getDefaultNetwork();
if (defaultNetwork != null && defaultNetwork.linkProperties != null) { if (defaultNai != null) {
info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName(); underlyingNetworks = new Network[] { defaultNai.network };
}
} else if (underlyingNetworks.length > 0) {
LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
if (linkProperties != null) {
info.primaryUnderlyingIface = linkProperties.getInterfaceName();
} }
} }
return info.primaryUnderlyingIface == null ? null : info; if (underlyingNetworks != null && underlyingNetworks.length > 0) {
List<String> interfaces = new ArrayList<>();
for (Network network : underlyingNetworks) {
LinkProperties lp = getLinkProperties(network);
if (lp != null) {
for (String iface : lp.getAllInterfaceNames()) {
if (!TextUtils.isEmpty(iface)) {
interfaces.add(iface);
}
}
}
}
if (!interfaces.isEmpty()) {
info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]);
}
}
return info.underlyingIfaces == null ? null : info;
} }
/** /**
@@ -6776,8 +6788,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
/** /**
* Notify NetworkStatsService that the set of active ifaces has changed, or that one of the * Notify NetworkStatsService and NetworkStatsFactory that the set of active ifaces has changed,
* properties tracked by NetworkStatsService on an active iface has changed. * or that one of the active iface's trackedproperties has changed.
*/ */
private void notifyIfacesChangedForNetworkStats() { private void notifyIfacesChangedForNetworkStats() {
ensureRunningOnConnectivityServiceThread(); ensureRunningOnConnectivityServiceThread();
@@ -6786,11 +6798,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (activeLinkProperties != null) { if (activeLinkProperties != null) {
activeIface = activeLinkProperties.getInterfaceName(); activeIface = activeLinkProperties.getInterfaceName();
} }
// CAUTION: Ordering matters between updateVpnInfos() and forceUpdateIfaces(), which
// triggers a new poll. Trigger the poll first to ensure a snapshot is taken before
// switching to the new state. This ensures that traffic does not get mis-attributed to
// incorrect apps (including VPN app).
try { try {
mStatsService.forceUpdateIfaces( mStatsService.forceUpdateIfaces(
getDefaultNetworks(), getAllVpnInfo(), getAllNetworkState(), activeIface); getDefaultNetworks(), getAllNetworkState(), activeIface);
} catch (Exception ignored) { } catch (Exception ignored) {
} }
NetworkStatsFactory.updateVpnInfos(getAllVpnInfo());
} }
@Override @Override

View File

@@ -36,7 +36,6 @@ import static android.net.NetworkStats.SET_DEFAULT;
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_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.TrafficStats.UID_TETHERING; import static android.net.TrafficStats.UID_TETHERING;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
@@ -1231,7 +1230,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
public NetworkStats getNetworkStatsDetail() { public NetworkStats getNetworkStatsDetail() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try { try {
return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null); return mStatsFactory.readNetworkStatsDetail();
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
@@ -1540,7 +1539,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) { public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try { try {
return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL, null); return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }

View File

@@ -16,6 +16,7 @@
package com.android.server.net; package com.android.server.net;
import static android.net.NetworkStats.INTERFACES_ALL;
import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.TAG_NONE;
@@ -33,6 +34,7 @@ import android.os.SystemClock;
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.VpnInfo;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader; import com.android.internal.util.ProcFileReader;
@@ -70,11 +72,25 @@ public class NetworkStatsFactory {
private INetd mNetdService; private INetd mNetdService;
// A persistent Snapshot since device start for eBPF stats /**
@GuardedBy("mPersistSnapshot") * Guards persistent data access in this class
private final NetworkStats mPersistSnapshot; *
* <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out
* to other code that will acquire other locks within the system server. See b/134244752.
*/
private static final Object sPersistentDataLock = new Object();
/** Set containing info about active VPNs and their underlying networks. */
private static volatile VpnInfo[] sVpnInfos = new VpnInfo[0];
// A persistent snapshot of cumulative stats since device start
@GuardedBy("sPersistentDataLock")
private NetworkStats mPersistSnapshot;
// The persistent snapshot of tun and 464xlat adjusted stats since device start
@GuardedBy("sPersistentDataLock")
private NetworkStats mTunAnd464xlatAdjustedStats;
// TODO: only do adjustments in NetworkStatsService and remove this.
/** /**
* (Stacked interface) -> (base interface) association for all connected ifaces since boot. * (Stacked interface) -> (base interface) association for all connected ifaces since boot.
* *
@@ -90,6 +106,24 @@ public class NetworkStatsFactory {
} }
} }
/**
* Set active VPN information for data usage migration purposes
*
* <p>Traffic on TUN-based VPNs inherently all appear to be originated from the VPN providing
* app's UID. This method is used to support migration of VPN data usage, ensuring data is
* accurately billed to the real owner of the traffic.
*
* @param vpnArray The snapshot of the currently-running VPNs.
*/
public static void updateVpnInfos(VpnInfo[] vpnArray) {
sVpnInfos = vpnArray.clone();
}
@VisibleForTesting
public static VpnInfo[] getVpnInfos() {
return sVpnInfos.clone();
}
/** /**
* Get a set of interfaces containing specified ifaces and stacked interfaces. * Get a set of interfaces containing specified ifaces and stacked interfaces.
* *
@@ -146,6 +180,7 @@ public class NetworkStatsFactory {
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats; mUseBpfStats = useBpfStats;
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1); mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
} }
public NetworkStats readBpfNetworkStatsDev() throws IOException { public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -264,22 +299,24 @@ public class NetworkStatsFactory {
} }
public NetworkStats readNetworkStatsDetail() throws IOException { public NetworkStats readNetworkStatsDetail() throws IOException {
return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null); return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
} }
public NetworkStats readNetworkStatsDetail(int limitUid, String[] limitIfaces, int limitTag, /**
NetworkStats lastStats) throws IOException { * Reads the detailed UID stats based on the provided parameters
final NetworkStats stats = *
readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats); * @param limitUid the UID to limit this query to
* @param limitIfaces the interfaces to limit this query to. Use {@link
// No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap. * NetworkStats.INTERFACES_ALL} to select all interfaces
// TODO: remove this and only apply adjustments in NetworkStatsService. * @param limitTag the tags to limit this query to
stats.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats); * @return the NetworkStats instance containing network statistics at the present time.
*/
return stats; public NetworkStats readNetworkStatsDetail(
int limitUid, @Nullable String[] limitIfaces, int limitTag) throws IOException {
return readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag);
} }
@GuardedBy("mPersistSnapshot") @GuardedBy("sPersistentDataLock")
private void requestSwapActiveStatsMapLocked() throws RemoteException { private void requestSwapActiveStatsMapLocked() throws RemoteException {
// Ask netd to do a active map stats swap. When the binder call successfully returns, // Ask netd to do a active map stats swap. When the binder call successfully returns,
// the system server should be able to safely read and clean the inactive map // the system server should be able to safely read and clean the inactive map
@@ -292,19 +329,20 @@ public class NetworkStatsFactory {
} }
} }
// TODO: delete the lastStats parameter private NetworkStats readNetworkStatsDetailInternal(
private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces, int limitUid, String[] limitIfaces, int limitTag) throws IOException {
int limitTag, NetworkStats lastStats) throws IOException { // In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other
if (USE_NATIVE_PARSING) { // code that will acquire other locks within the system server. See b/134244752.
final NetworkStats stats; synchronized (sPersistentDataLock) {
if (lastStats != null) { // Take a reference. If this gets swapped out, we still have the old reference.
stats = lastStats; final VpnInfo[] vpnArray = sVpnInfos;
stats.setElapsedRealtime(SystemClock.elapsedRealtime()); // Take a defensive copy. mPersistSnapshot is mutated in some cases below
} else { final NetworkStats prev = mPersistSnapshot.clone();
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
} if (USE_NATIVE_PARSING) {
if (mUseBpfStats) { final NetworkStats stats =
synchronized (mPersistSnapshot) { new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */);
if (mUseBpfStats) {
try { try {
requestSwapActiveStatsMapLocked(); requestSwapActiveStatsMapLocked();
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -313,32 +351,66 @@ public class NetworkStatsFactory {
// Stats are always read from the inactive map, so they must be read after the // Stats are always read from the inactive map, so they must be read after the
// swap // swap
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL, if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
null, TAG_ALL, mUseBpfStats) != 0) { INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats"); throw new IOException("Failed to parse network stats");
} }
// BPF stats are incremental; fold into mPersistSnapshot.
mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime()); mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
mPersistSnapshot.combineAllValues(stats); mPersistSnapshot.combineAllValues(stats);
NetworkStats result = mPersistSnapshot.clone(); } else {
result.filter(limitUid, limitIfaces, limitTag); if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
return result; INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats");
}
if (SANITY_CHECK_NATIVE) {
final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
UID_ALL, INTERFACES_ALL, TAG_ALL);
assertEquals(javaStats, stats);
}
mPersistSnapshot = stats;
} }
} else { } else {
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid, mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL,
limitIfaces, limitTag, mUseBpfStats) != 0) { TAG_ALL);
throw new IOException("Failed to parse network stats");
}
if (SANITY_CHECK_NATIVE) {
final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
limitIfaces, limitTag);
assertEquals(javaStats, stats);
}
return stats;
} }
} else {
return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag); NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
// Filter return values
adjustedStats.filter(limitUid, limitIfaces, limitTag);
return adjustedStats;
} }
} }
@GuardedBy("sPersistentDataLock")
private NetworkStats adjustForTunAnd464Xlat(
NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) {
// Calculate delta from last snapshot
final NetworkStats delta = uidDetailStats.subtract(previousStats);
// Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
// network, the overhead is their fault.
// No locking here: apply464xlatAdjustments behaves fine with an add-only
// ConcurrentHashMap.
delta.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
// Migrate data usage over a VPN to the TUN network.
for (VpnInfo info : vpnArray) {
delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
}
// Filter out debug entries as that may lead to over counting.
delta.filterDebugEntries();
// Update mTunAnd464xlatAdjustedStats with migrated delta.
mTunAnd464xlatAdjustedStats.combineAllValues(delta);
mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
return mTunAnd464xlatAdjustedStats.clone();
}
/** /**
* Parse and return {@link NetworkStats} with UID-level details. Values are * Parse and return {@link NetworkStats} with UID-level details. Values are
* expected to monotonically increase since device boot. * expected to monotonically increase since device boot.

View File

@@ -39,7 +39,6 @@ import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnInfo;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -104,9 +103,9 @@ class NetworkStatsObservers {
public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot, public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
ArrayMap<String, NetworkIdentitySet> activeIfaces, ArrayMap<String, NetworkIdentitySet> activeIfaces,
ArrayMap<String, NetworkIdentitySet> activeUidIfaces, ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
VpnInfo[] vpnArray, long currentTime) { long currentTime) {
StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces, StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces,
activeUidIfaces, vpnArray, currentTime); activeUidIfaces, currentTime);
getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext)); getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext));
} }
@@ -354,7 +353,7 @@ class NetworkStatsObservers {
// thread will update it. We pass a null VPN array because usage is aggregated by uid // thread will update it. We pass a null VPN array because usage is aggregated by uid
// for this snapshot, so VPN traffic can't be reattributed to responsible apps. // for this snapshot, so VPN traffic can't be reattributed to responsible apps.
mRecorder.recordSnapshotLocked(statsContext.mXtSnapshot, statsContext.mActiveIfaces, mRecorder.recordSnapshotLocked(statsContext.mXtSnapshot, statsContext.mActiveIfaces,
null /* vpnArray */, statsContext.mCurrentTime); statsContext.mCurrentTime);
} }
/** /**
@@ -396,7 +395,7 @@ class NetworkStatsObservers {
// thread will update it. We pass the VPN info so VPN traffic is reattributed to // thread will update it. We pass the VPN info so VPN traffic is reattributed to
// responsible apps. // responsible apps.
mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces, mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces,
statsContext.mVpnArray, statsContext.mCurrentTime); statsContext.mCurrentTime);
} }
/** /**
@@ -427,18 +426,16 @@ class NetworkStatsObservers {
NetworkStats mUidSnapshot; NetworkStats mUidSnapshot;
ArrayMap<String, NetworkIdentitySet> mActiveIfaces; ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces; ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
VpnInfo[] mVpnArray;
long mCurrentTime; long mCurrentTime;
StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot, StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
ArrayMap<String, NetworkIdentitySet> activeIfaces, ArrayMap<String, NetworkIdentitySet> activeIfaces,
ArrayMap<String, NetworkIdentitySet> activeUidIfaces, ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
VpnInfo[] vpnArray, long currentTime) { long currentTime) {
mXtSnapshot = xtSnapshot; mXtSnapshot = xtSnapshot;
mUidSnapshot = uidSnapshot; mUidSnapshot = uidSnapshot;
mActiveIfaces = activeIfaces; mActiveIfaces = activeIfaces;
mActiveUidIfaces = activeUidIfaces; mActiveUidIfaces = activeUidIfaces;
mVpnArray = vpnArray;
mCurrentTime = currentTime; mCurrentTime = currentTime;
} }
} }

View File

@@ -23,7 +23,6 @@ import static android.text.format.DateUtils.YEAR_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
import android.net.NetworkStats; import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
@@ -37,14 +36,13 @@ import android.util.MathUtils;
import android.util.Slog; import android.util.Slog;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.FileRotator; import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import libcore.io.IoUtils;
import com.google.android.collect.Sets; import com.google.android.collect.Sets;
import libcore.io.IoUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.File; import java.io.File;
@@ -202,18 +200,12 @@ public class NetworkStatsRecorder {
} }
/** /**
* Record any delta that occurred since last {@link NetworkStats} snapshot, * Record any delta that occurred since last {@link NetworkStats} snapshot, using the given
* using the given {@link Map} to identify network interfaces. First * {@link Map} to identify network interfaces. First snapshot is considered bootstrap, and is
* snapshot is considered bootstrap, and is not counted as delta. * not counted as delta.
*
* @param vpnArray Optional info about the currently active VPN, if any. This is used to
* redistribute traffic from the VPN app to the underlying responsible apps.
* This should always be set to null if the provided snapshot is aggregated
* across all UIDs (e.g. contains UID_ALL buckets), regardless of VPN state.
*/ */
public void recordSnapshotLocked(NetworkStats snapshot, public void recordSnapshotLocked(NetworkStats snapshot,
Map<String, NetworkIdentitySet> ifaceIdent, @Nullable VpnInfo[] vpnArray, Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
long currentTimeMillis) {
final HashSet<String> unknownIfaces = Sets.newHashSet(); final HashSet<String> unknownIfaces = Sets.newHashSet();
// skip recording when snapshot missing // skip recording when snapshot missing
@@ -232,12 +224,6 @@ public class NetworkStatsRecorder {
final long end = currentTimeMillis; final long end = currentTimeMillis;
final long start = end - delta.getElapsedRealtime(); final long start = end - delta.getElapsedRealtime();
if (vpnArray != null) {
for (VpnInfo info : vpnArray) {
delta.migrateTun(info.ownerUid, info.vpnIface, info.primaryUnderlyingIface);
}
}
NetworkStats.Entry entry = null; NetworkStats.Entry entry = null;
for (int i = 0; i < delta.size(); i++) { for (int i = 0; i < delta.size(); i++) {
entry = delta.getValues(i, entry); entry = delta.getValues(i, entry);

View File

@@ -130,7 +130,6 @@ 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.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;
import com.android.internal.util.FileRotator; import com.android.internal.util.FileRotator;
@@ -266,10 +265,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@GuardedBy("mStatsLock") @GuardedBy("mStatsLock")
private Network[] mDefaultNetworks = new Network[0]; private Network[] mDefaultNetworks = new Network[0];
/** Set containing info about active VPNs and their underlying networks. */
@GuardedBy("mStatsLock")
private VpnInfo[] mVpnInfos = new VpnInfo[0];
private final DropBoxNonMonotonicObserver mNonMonotonicObserver = private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver(); new DropBoxNonMonotonicObserver();
@@ -857,7 +852,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public void forceUpdateIfaces( public void forceUpdateIfaces(
Network[] defaultNetworks, Network[] defaultNetworks,
VpnInfo[] vpnArray,
NetworkState[] networkStates, NetworkState[] networkStates,
String activeIface) { String activeIface) {
checkNetworkStackPermission(mContext); checkNetworkStackPermission(mContext);
@@ -865,7 +859,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
updateIfaces(defaultNetworks, vpnArray, networkStates, activeIface); updateIfaces(defaultNetworks, networkStates, activeIface);
} finally { } finally {
Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token);
} }
@@ -1131,13 +1125,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private void updateIfaces( private void updateIfaces(
Network[] defaultNetworks, Network[] defaultNetworks,
VpnInfo[] vpnArray,
NetworkState[] networkStates, NetworkState[] networkStates,
String activeIface) { String activeIface) {
synchronized (mStatsLock) { synchronized (mStatsLock) {
mWakeLock.acquire(); mWakeLock.acquire();
try { try {
mVpnInfos = vpnArray;
mActiveIface = activeIface; mActiveIface = activeIface;
updateIfacesLocked(defaultNetworks, networkStates); updateIfacesLocked(defaultNetworks, networkStates);
} finally { } finally {
@@ -1147,10 +1139,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
/** /**
* Inspect all current {@link NetworkState} to derive mapping from {@code * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
* iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo} * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
* are active on a single {@code iface}, they are combined under a single * they are combined under a single {@link NetworkIdentitySet}.
* {@link NetworkIdentitySet}.
*/ */
@GuardedBy("mStatsLock") @GuardedBy("mStatsLock")
private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) { private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) {
@@ -1267,27 +1258,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic
// can't be reattributed to responsible apps. // can't be reattributed to responsible apps.
Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev"); Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
mDevRecorder.recordSnapshotLocked( mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
devSnapshot, mActiveIfaces, null /* vpnArray */, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK); Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "recordXt"); Trace.traceBegin(TRACE_TAG_NETWORK, "recordXt");
mXtRecorder.recordSnapshotLocked( mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
xtSnapshot, mActiveIfaces, null /* vpnArray */, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK); Trace.traceEnd(TRACE_TAG_NETWORK);
// For per-UID stats, pass the VPN info so VPN traffic is reattributed to responsible apps. // For per-UID stats, pass the VPN info so VPN traffic is reattributed to responsible apps.
VpnInfo[] vpnArray = mVpnInfos;
Trace.traceBegin(TRACE_TAG_NETWORK, "recordUid"); Trace.traceBegin(TRACE_TAG_NETWORK, "recordUid");
mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime); mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK); Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "recordUidTag"); Trace.traceBegin(TRACE_TAG_NETWORK, "recordUidTag");
mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime); mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK); Trace.traceEnd(TRACE_TAG_NETWORK);
// We need to make copies of member fields that are sent to the observer to avoid // We need to make copies of member fields that are sent to the observer to avoid
// a race condition between the service handler thread and the observer's // a race condition between the service handler thread and the observer's
mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces), mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces),
new ArrayMap<>(mActiveUidIfaces), vpnArray, currentTime); new ArrayMap<>(mActiveUidIfaces), currentTime);
} }
/** /**
@@ -1660,8 +1648,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*/ */
private NetworkStats getNetworkStatsUidDetail(String[] ifaces) private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
throws RemoteException { throws RemoteException {
// TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here.
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL, final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
ifaces); ifaces);

View File

@@ -569,7 +569,7 @@ public class NetworkStatsTest {
.addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface)); delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface});
assertEquals(20, delta.size()); assertEquals(20, delta.size());
// tunIface and TEST_IFACE entries are not changed. // tunIface and TEST_IFACE entries are not changed.
@@ -650,7 +650,7 @@ public class NetworkStatsTest {
.addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface)); delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(9, delta.size()); assertEquals(9, delta.size());
// tunIface entries should not be changed. // tunIface entries should not be changed.
@@ -812,6 +812,37 @@ public class NetworkStatsTest {
assertEquals(entry2, stats.getValues(1, null)); assertEquals(entry2, stats.getValues(1, null));
} }
@Test
public void testFilterDebugEntries() {
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_DBG_VPN_IN, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry3 = new NetworkStats.Entry(
"test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats.Entry entry4 = new NetworkStats.Entry(
"test2", 10101, SET_DBG_VPN_OUT, TAG_NONE, 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.filterDebugEntries();
assertEquals(2, stats.size());
assertEquals(entry1, stats.getValues(0, null));
assertEquals(entry3, stats.getValues(1, null));
}
@Test @Test
public void testApply464xlatAdjustments() { public void testApply464xlatAdjustments() {
final String v4Iface = "v4-wlan0"; final String v4Iface = "v4-wlan0";

View File

@@ -192,6 +192,7 @@ import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn; import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.net.NetworkStatsFactory;
import com.android.testutils.HandlerUtilsKt; import com.android.testutils.HandlerUtilsKt;
import org.junit.After; import org.junit.After;
@@ -4901,9 +4902,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, atLeastOnce()) verify(mStatsService, atLeastOnce())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyCell), eq(onlyCell),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(MOBILE_IFNAME)); eq(MOBILE_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
// Default network switch should update ifaces. // Default network switch should update ifaces.
@@ -4914,9 +4915,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, atLeastOnce()) verify(mStatsService, atLeastOnce())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyWifi), eq(onlyWifi),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(WIFI_IFNAME)); eq(WIFI_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
// Disconnect should update ifaces. // Disconnect should update ifaces.
@@ -4925,9 +4926,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, atLeastOnce()) verify(mStatsService, atLeastOnce())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyCell), eq(onlyCell),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(MOBILE_IFNAME)); eq(MOBILE_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
// Metered change should update ifaces // Metered change should update ifaces
@@ -4936,9 +4937,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, atLeastOnce()) verify(mStatsService, atLeastOnce())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyCell), eq(onlyCell),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(MOBILE_IFNAME)); eq(MOBILE_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
@@ -4946,9 +4947,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, atLeastOnce()) verify(mStatsService, atLeastOnce())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyCell), eq(onlyCell),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(MOBILE_IFNAME)); eq(MOBILE_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
// Captive portal change shouldn't update ifaces // Captive portal change shouldn't update ifaces
@@ -4957,9 +4958,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, never()) verify(mStatsService, never())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyCell), eq(onlyCell),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(MOBILE_IFNAME)); eq(MOBILE_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
// Roaming change should update ifaces // Roaming change should update ifaces
@@ -4968,9 +4969,9 @@ public class ConnectivityServiceTest {
verify(mStatsService, atLeastOnce()) verify(mStatsService, atLeastOnce())
.forceUpdateIfaces( .forceUpdateIfaces(
eq(onlyCell), eq(onlyCell),
eq(new VpnInfo[0]),
any(NetworkState[].class), any(NetworkState[].class),
eq(MOBILE_IFNAME)); eq(MOBILE_IFNAME));
assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService); reset(mStatsService);
} }

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.net;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.ROAMING_YES;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
import com.android.internal.net.VpnInfo;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
static final String TEST_IFACE = "test0";
static final String TEST_IFACE2 = "test1";
static final String TUN_IFACE = "test_nss_tun0";
static final int UID_RED = 1001;
static final int UID_BLUE = 1002;
static final int UID_GREEN = 1003;
static final int UID_VPN = 1004;
void assertValues(NetworkStats stats, String iface, int uid, long rxBytes,
long rxPackets, long txBytes, long txPackets) {
assertValues(
stats, iface, uid, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
rxBytes, rxPackets, txBytes, txPackets, 0);
}
void assertValues(NetworkStats stats, String iface, int uid, int set, int tag,
int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
final NetworkStats.Entry entry = new NetworkStats.Entry();
final int[] sets;
if (set == SET_ALL) {
sets = new int[] {SET_ALL, SET_DEFAULT, SET_FOREGROUND};
} else {
sets = new int[] {set};
}
final int[] roamings;
if (roaming == ROAMING_ALL) {
roamings = new int[] {ROAMING_ALL, ROAMING_YES, ROAMING_NO};
} else {
roamings = new int[] {roaming};
}
final int[] meterings;
if (metered == METERED_ALL) {
meterings = new int[] {METERED_ALL, METERED_YES, METERED_NO};
} else {
meterings = new int[] {metered};
}
final int[] defaultNetworks;
if (defaultNetwork == DEFAULT_NETWORK_ALL) {
defaultNetworks =
new int[] {DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, DEFAULT_NETWORK_NO};
} else {
defaultNetworks = new int[] {defaultNetwork};
}
for (int s : sets) {
for (int r : roamings) {
for (int m : meterings) {
for (int d : defaultNetworks) {
final int i = stats.findIndex(iface, uid, s, tag, m, r, d);
if (i != -1) {
entry.add(stats.getValues(i, null));
}
}
}
}
}
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
assertEquals("unexpected txBytes", txBytes, entry.txBytes);
assertEquals("unexpected txPackets", txPackets, entry.txPackets);
assertEquals("unexpected operations", operations, entry.operations);
}
VpnInfo createVpnInfo(String[] underlyingIfaces) {
VpnInfo info = new VpnInfo();
info.ownerUid = UID_VPN;
info.vpnIface = TUN_IFACE;
info.underlyingIfaces = underlyingIfaces;
return info;
}
}

View File

@@ -16,8 +16,11 @@
package com.android.server.net; package com.android.server.net;
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.METERED_ALL;
import static android.net.NetworkStats.METERED_NO; import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_DEFAULT;
@@ -39,6 +42,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R; import com.android.frameworks.tests.net.R;
import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import libcore.io.Streams; import libcore.io.Streams;
@@ -54,12 +58,12 @@ import java.io.FileWriter;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/** /** Tests for {@link NetworkStatsFactory}. */
* Tests for {@link NetworkStatsFactory}.
*/
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class NetworkStatsFactoryTest { public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
private static final String CLAT_PREFIX = "v4-";
private File mTestProc; private File mTestProc;
private NetworkStatsFactory mFactory; private NetworkStatsFactory mFactory;
@@ -75,6 +79,8 @@ public class NetworkStatsFactoryTest {
// related to networkStatsFactory is compiled to a minimal native library and loaded here. // related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni"); System.loadLibrary("networkstatsfactorytestjni");
mFactory = new NetworkStatsFactory(mTestProc, false); mFactory = new NetworkStatsFactory(mTestProc, false);
NetworkStatsFactory.updateVpnInfos(new VpnInfo[0]);
NetworkStatsFactory.clearStackedIfaces();
} }
@After @After
@@ -98,6 +104,236 @@ public class NetworkStatsFactoryTest {
assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L); assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L);
} }
@Test
public void vpnRewriteTrafficThroughItself() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
//
// 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
// over VPN.
// 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
// over VPN.
//
// VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic
final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_rewrite_through_self);
assertValues(tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 0);
assertValues(tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 0);
assertValues(tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 0);
assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 260L, 26L);
}
@Test
public void vpnWithClat() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
NetworkStatsFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
// over VPN.
// 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
// over VPN.
// VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over v4-WiFi, and clat
// added 20 bytes per packet of extra overhead
//
// For 1650 bytes sent over v4-WiFi, 4650 bytes were actually sent over WiFi, which is
// expected to be split as follows:
// UID_RED: 1000 bytes, 100 packets
// UID_BLUE: 500 bytes, 50 packets
// UID_VPN: 3150 bytes, 0 packets
//
// For 3300 bytes received over v4-WiFi, 9300 bytes were actually sent over WiFi, which is
// expected to be split as follows:
// UID_RED: 2000 bytes, 200 packets
// UID_BLUE: 1000 bytes, 100 packets
// UID_VPN: 6300 bytes, 0 packets
final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_with_clat);
assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_RED, 2000L, 200L, 1000, 100L);
assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_VPN, 6300L, 0L, 3150L, 0L);
}
@Test
public void vpnWithOneUnderlyingIface() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
// over VPN.
// 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
// over VPN.
// VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi.
// Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
// attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
// Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
// attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying);
assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L);
}
@Test
public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
// over VPN.
// 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
// over VPN.
// Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun
// interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500
// packets) from it. Including overhead that is 6600/5500 bytes.
// VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi.
// Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
// attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN.
// Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
// attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN.
final NetworkStats tunStats =
parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic);
assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 5800L, 500L, 6750L, 600L);
}
@Test
public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
// 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN.
// VPN sent/received 1000 bytes (100 packets) over WiFi.
// Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE,
// with nothing attributed to UID_VPN for both rx/tx traffic.
final NetworkStats tunStats =
parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_compression);
assertValues(tunStats, TEST_IFACE, UID_RED, 250L, 25L, 250L, 25L);
assertValues(tunStats, TEST_IFACE, UID_BLUE, 750L, 75L, 750L, 75L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
}
@Test
public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is duplicating traffic across both WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN.
// VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total).
// Of 8800 bytes over WiFi/Cell, expect:
// - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE.
// - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID.
final NetworkStats tunStats =
parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_duplication);
assertValues(tunStats, TEST_IFACE, UID_RED, 500L, 50L, 500L, 50L);
assertValues(tunStats, TEST_IFACE, UID_BLUE, 500L, 50L, 500L, 50L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 1200L, 100L, 1200L, 100L);
assertValues(tunStats, TEST_IFACE2, UID_RED, 500L, 50L, 500L, 50L);
assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 500L, 50L);
assertValues(tunStats, TEST_IFACE2, UID_VPN, 1200L, 100L, 1200L, 100L);
}
@Test
public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over
// VPN.
// VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell.
// And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell.
// For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx)
// traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell.
//
// For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic.
// And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic.
final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split);
assertValues(tunStats, TEST_IFACE, UID_RED, 300L, 30L, 600L, 60L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 30L, 0L, 60L, 0L);
assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 400L, 40L);
assertValues(tunStats, TEST_IFACE2, UID_VPN, 20L, 0L, 40L, 0L);
}
@Test
public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface:
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
// VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell.
// For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both
// rx/tx.
// UID_VPN gets nothing attributed to it (avoiding negative stats).
final NetworkStats tunStats =
parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split_compression);
assertValues(tunStats, TEST_IFACE, UID_RED, 600L, 60L, 600L, 60L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 200L, 20L);
assertValues(tunStats, TEST_IFACE2, UID_VPN, 0L, 0L, 0L, 0L);
}
@Test
public void vpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
NetworkStatsFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
// VPN sent/received 1100 bytes (100 packets) over Cell.
// Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic.
final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_incorrect_iface);
assertValues(tunStats, TEST_IFACE, UID_RED, 0L, 0L, 0L, 0L);
assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
assertValues(tunStats, TEST_IFACE2, UID_RED, 0L, 0L, 0L, 0L);
assertValues(tunStats, TEST_IFACE2, UID_VPN, 1100L, 100L, 1100L, 100L);
}
@Test @Test
public void testKernelTags() throws Exception { public void testKernelTags() throws Exception {
assertEquals(0, kernelToTag("0x0000000000000000")); assertEquals(0, kernelToTag("0x0000000000000000"));
@@ -146,7 +382,7 @@ public class NetworkStatsFactoryTest {
} }
@Test @Test
public void testDoubleClatAccounting() throws Exception { public void testDoubleClatAccountingSimple() throws Exception {
NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0"); NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
// xt_qtaguid_with_clat_simple is a synthetic file that simulates // xt_qtaguid_with_clat_simple is a synthetic file that simulates
@@ -154,12 +390,17 @@ public class NetworkStatsFactoryTest {
// - 41 sent 464xlat packets of size 100 bytes // - 41 sent 464xlat packets of size 100 bytes
// - no other traffic on base interface for root uid. // - no other traffic on base interface for root uid.
NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple); NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple);
assertEquals(4, stats.size()); assertEquals(3, stats.size());
assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L); assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L);
assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L); assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L);
}
stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat); @Test
public void testDoubleClatAccounting() throws Exception {
NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
assertEquals(42, stats.size()); assertEquals(42, stats.size());
assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L); assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L);
@@ -272,11 +513,19 @@ public class NetworkStatsFactoryTest {
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, assertStatsEntry(stats, iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
DEFAULT_NETWORK_NO); rxBytes, rxPackets, txBytes, txPackets);
}
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets) {
final int i = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork);
if (i < 0) { if (i < 0) {
fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)", fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d, metered:"
iface, uid, set, tag)); + " %d, roaming: %d, defaultNetwork: %d)",
iface, uid, set, tag, metered, roaming, defaultNetwork));
} }
final NetworkStats.Entry entry = stats.getValues(i, null); final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);

View File

@@ -52,7 +52,6 @@ import android.util.ArrayMap;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnInfo;
import com.android.server.net.NetworkStatsServiceTest.LatchedHandler; import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
import com.android.testutils.HandlerUtilsKt; import com.android.testutils.HandlerUtilsKt;
@@ -93,8 +92,6 @@ public class NetworkStatsObserversTest {
private static final long BASE_BYTES = 7 * MB_IN_BYTES; private static final long BASE_BYTES = 7 * MB_IN_BYTES;
private static final int INVALID_TYPE = -1; private static final int INVALID_TYPE = -1;
private static final VpnInfo[] VPN_INFO = new VpnInfo[0];
private long mElapsedRealtime; private long mElapsedRealtime;
private HandlerThread mObserverHandlerThread; private HandlerThread mObserverHandlerThread;
@@ -247,8 +244,7 @@ public class NetworkStatsObserversTest {
NetworkStats uidSnapshot = null; NetworkStats uidSnapshot = null;
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
} }
@@ -271,15 +267,13 @@ public class NetworkStatsObserversTest {
.addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null; NetworkStats uidSnapshot = null;
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
// Delta // Delta
xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
.addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L); .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
} }
@@ -303,16 +297,14 @@ public class NetworkStatsObserversTest {
.addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null; NetworkStats uidSnapshot = null;
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
// Delta // Delta
xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */) xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
.addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L, .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
BASE_BYTES + THRESHOLD_BYTES, 22L); BASE_BYTES + THRESHOLD_BYTES, 22L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
} }
@@ -337,8 +329,7 @@ public class NetworkStatsObserversTest {
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
// Delta // Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -346,8 +337,7 @@ public class NetworkStatsObserversTest {
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
} }
@@ -372,8 +362,7 @@ public class NetworkStatsObserversTest {
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
// Delta // Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -381,8 +370,7 @@ public class NetworkStatsObserversTest {
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
} }
@@ -406,8 +394,7 @@ public class NetworkStatsObserversTest {
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
// Delta // Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -415,8 +402,7 @@ public class NetworkStatsObserversTest {
DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L, DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
} }
@@ -441,8 +427,7 @@ public class NetworkStatsObserversTest {
.addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
// Delta // Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -450,8 +435,7 @@ public class NetworkStatsObserversTest {
ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats( mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
VPN_INFO, TEST_START);
waitForObserverToIdle(); waitForObserverToIdle();
} }

View File

@@ -23,7 +23,6 @@ import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.ConnectivityManager.TYPE_WIMAX;
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_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.INTERFACES_ALL;
@@ -42,7 +41,6 @@ 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;
import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_REMOVED;
@@ -60,7 +58,6 @@ 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.ArgumentMatchers.eq;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -98,7 +95,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils; 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;
@@ -129,13 +125,9 @@ import java.util.Objects;
*/ */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class NetworkStatsServiceTest { public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
private static final String TAG = "NetworkStatsServiceTest"; private static final String TAG = "NetworkStatsServiceTest";
private static final String TEST_IFACE = "test0";
private static final String TEST_IFACE2 = "test1";
private static final String TUN_IFACE = "test_nss_tun0";
private static final long TEST_START = 1194220800000L; private static final long TEST_START = 1194220800000L;
private static final String IMSI_1 = "310004"; private static final String IMSI_1 = "310004";
@@ -146,11 +138,6 @@ public class NetworkStatsServiceTest {
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
private static final int UID_RED = 1001;
private static final int UID_BLUE = 1002;
private static final int UID_GREEN = 1003;
private static final int UID_VPN = 1004;
private static final Network WIFI_NETWORK = new Network(100); private static final Network WIFI_NETWORK = new Network(100);
private static final Network MOBILE_NETWORK = new Network(101); private static final Network MOBILE_NETWORK = new Network(101);
private static final Network VPN_NETWORK = new Network(102); private static final Network VPN_NETWORK = new Network(102);
@@ -217,10 +204,12 @@ public class NetworkStatsServiceTest {
expectSystemReady(); expectSystemReady();
mService.systemReady(); mService.systemReady();
// Verify that system ready fetches realtime stats
verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
mSession = mService.openSession(); mSession = mService.openSession();
assertNotNull("openSession() failed", mSession); assertNotNull("openSession() failed", mSession);
// catch INetworkManagementEventObserver during systemReady() // catch INetworkManagementEventObserver during systemReady()
ArgumentCaptor<INetworkManagementEventObserver> networkObserver = ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
ArgumentCaptor.forClass(INetworkManagementEventObserver.class); ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
@@ -252,7 +241,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// verify service has empty history for wifi // verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -296,7 +285,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// verify service has empty history for wifi // verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -370,8 +359,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// modify some number on wifi, and trigger poll event // modify some number on wifi, and trigger poll event
incrementCurrentTime(2 * HOUR_IN_MILLIS); incrementCurrentTime(2 * HOUR_IN_MILLIS);
@@ -412,8 +400,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// create some traffic on first network // create some traffic on first network
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -448,7 +435,7 @@ public class NetworkStatsServiceTest {
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
forcePollAndWaitForIdle(); forcePollAndWaitForIdle();
@@ -488,8 +475,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some traffic // create some traffic
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -547,8 +533,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// create some traffic // create some traffic
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -575,7 +560,7 @@ public class NetworkStatsServiceTest {
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
forcePollAndWaitForIdle(); forcePollAndWaitForIdle();
@@ -605,8 +590,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some traffic for two apps // create some traffic for two apps
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -664,7 +648,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
NetworkStats.Entry entry1 = new NetworkStats.Entry( NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L); TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -708,7 +692,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
NetworkStats.Entry uidStats = new NetworkStats.Entry( NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -733,11 +717,17 @@ public class NetworkStatsServiceTest {
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter); NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces -> // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations:
ifaces != null && ifaces.length == 2 // 1) NetworkStatsService#systemReady from #setUp.
&& ArrayUtils.contains(ifaces, TEST_IFACE) // 2) mService#forceUpdateIfaces in the test above.
&& ArrayUtils.contains(ifaces, stackedIface))); //
// Additionally, we should have one call from the above call to mService#getDetailedUidStats
// with the augmented ifaceFilter
verify(mNetManager, times(2)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
verify(mNetManager, times(1)).getNetworkStatsUidDetail(
eq(UID_ALL), eq(NetworkStatsFactory.augmentWithStackedInterfaces(ifaceFilter)));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
assertEquals(2, stats.size()); assertEquals(2, stats.size());
assertEquals(uidStats, stats.getValues(0, null)); assertEquals(uidStats, stats.getValues(0, null));
assertEquals(tetheredStats1, stats.getValues(1, null)); assertEquals(tetheredStats1, stats.getValues(1, null));
@@ -752,8 +742,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some initial traffic // create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -810,8 +799,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some initial traffic // create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -851,8 +839,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// Create some traffic // Create some traffic
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -890,8 +877,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// create some tethering traffic // create some tethering traffic
incrementCurrentTime(HOUR_IN_MILLIS); incrementCurrentTime(HOUR_IN_MILLIS);
@@ -922,113 +908,6 @@ public class NetworkStatsServiceTest {
assertUidTotal(sTemplateImsi1, UID_TETHERING, 1920L, 14L, 384L, 2L, 0); assertUidTotal(sTemplateImsi1, UID_TETHERING, 1920L, 14L, 384L, 2L, 0);
} }
@Test
public void vpnWithOneUnderlyingIface() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
expectDefaultSettings();
NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
mService.forceUpdateIfaces(
new Network[] {WIFI_NETWORK, VPN_NETWORK},
vpnInfos,
networkStates,
getActiveIface(networkStates));
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
// 500 bytes (50 packets) were sent/received by UID_BLUE over VPN.
// VPN sent/received 1650 bytes (150 packets) over WiFi.
// Of 1650 bytes over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes attributed to
// UID_BLUE, and 150 bytes attributed to UID_VPN for both rx/tx traffic.
incrementCurrentTime(HOUR_IN_MILLIS);
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
.addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
.addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 500L, 50L, 500L, 50L, 1L)
.addValues(
TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1650L, 150L, 1650L, 150L, 2L));
forcePollAndWaitForIdle();
assertUidTotal(sTemplateWifi, UID_RED, 1000L, 100L, 1000L, 100L, 1);
assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1);
assertUidTotal(sTemplateWifi, UID_VPN, 150L, 0L, 150L, 0L, 2);
}
@Test
public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
expectDefaultSettings();
NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
mService.forceUpdateIfaces(
new Network[] {WIFI_NETWORK, VPN_NETWORK},
vpnInfos,
networkStates,
getActiveIface(networkStates));
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
// 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN.
// VPN sent/received 1000 bytes (100 packets) over WiFi.
// Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE,
// with nothing attributed to UID_VPN for both rx/tx traffic.
incrementCurrentTime(HOUR_IN_MILLIS);
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
.addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
.addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 3000L, 300L, 3000L, 300L, 1L)
.addValues(
TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 0L));
forcePollAndWaitForIdle();
assertUidTotal(sTemplateWifi, UID_RED, 250L, 25L, 250L, 25L, 0);
assertUidTotal(sTemplateWifi, UID_BLUE, 750L, 75L, 750L, 75L, 0);
assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
}
@Test
public void vpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
expectDefaultSettings();
NetworkState[] networkStates =
new NetworkState[] {
buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
};
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
mService.forceUpdateIfaces(
new Network[] {WIFI_NETWORK, VPN_NETWORK},
vpnInfos,
networkStates,
getActiveIface(networkStates));
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
// VPN sent/received 1100 bytes (100 packets) over Cell.
// Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic.
incrementCurrentTime(HOUR_IN_MILLIS);
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
.addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
.addValues(
TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 1100L, 100L, 1L));
forcePollAndWaitForIdle();
assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0);
assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 0L, 0L, 0L, 0L, 0);
assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1100L, 100L, 1100L, 100L, 1);
}
@Test @Test
public void testRegisterUsageCallback() throws Exception { public void testRegisterUsageCallback() throws Exception {
// pretend that wifi network comes online; service should ask about full // pretend that wifi network comes online; service should ask about full
@@ -1039,7 +918,7 @@ public class NetworkStatsServiceTest {
expectNetworkStatsUidDetail(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck(); expectBandwidthControlCheck();
mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// verify service has empty history for wifi // verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -1259,59 +1138,6 @@ public class NetworkStatsServiceTest {
} }
} }
private static void assertValues(NetworkStats stats, String iface, int uid, int set,
int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) {
final NetworkStats.Entry entry = new NetworkStats.Entry();
final int[] sets;
if (set == SET_ALL) {
sets = new int[] { SET_ALL, SET_DEFAULT, SET_FOREGROUND };
} else {
sets = new int[] { set };
}
final int[] roamings;
if (roaming == ROAMING_ALL) {
roamings = new int[] { ROAMING_ALL, ROAMING_YES, ROAMING_NO };
} else {
roamings = new int[] { roaming };
}
final int[] meterings;
if (metered == METERED_ALL) {
meterings = new int[] { METERED_ALL, METERED_YES, METERED_NO };
} else {
meterings = new int[] { metered };
}
final int[] defaultNetworks;
if (defaultNetwork == DEFAULT_NETWORK_ALL) {
defaultNetworks = new int[] { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES,
DEFAULT_NETWORK_NO };
} else {
defaultNetworks = new int[] { defaultNetwork };
}
for (int s : sets) {
for (int r : roamings) {
for (int m : meterings) {
for (int d : defaultNetworks) {
final int i = stats.findIndex(iface, uid, s, tag, m, r, d);
if (i != -1) {
entry.add(stats.getValues(i, null));
}
}
}
}
}
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
assertEquals("unexpected txBytes", txBytes, entry.txBytes);
assertEquals("unexpected txPackets", txPackets, entry.txPackets);
assertEquals("unexpected operations", operations, entry.operations);
}
private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes, private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes,
long rxPackets, long txBytes, long txPackets, int operations) { long rxPackets, long txBytes, long txPackets, int operations) {
final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
@@ -1377,14 +1203,6 @@ public class NetworkStatsServiceTest {
return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null); return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
} }
private static VpnInfo createVpnInfo(String underlyingIface) {
VpnInfo info = new VpnInfo();
info.ownerUid = UID_VPN;
info.vpnIface = TUN_IFACE;
info.primaryUnderlyingIface = underlyingIface;
return info;
}
private long getElapsedRealtime() { private long getElapsedRealtime() {
return mElapsedRealtime; return mElapsedRealtime;
} }

View File

@@ -0,0 +1,3 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,5 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
4 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,4 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test_nss_tun0 0x0 1002 0 3000 300 3000 300 0 0 0 0 0 0 0 0 0 0 0 0
4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,6 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
4 test_nss_tun0 0x0 1004 0 5000 500 6000 600 0 0 0 0 0 0 0 0 0 0 0 0
5 test0 0x0 1004 0 8800 800 0 0 0 0 0 0 0 0 0 0 0 0 0 0
6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,6 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
4 test_nss_tun0 0x0 1004 0 0 0 1600 160 0 0 0 0 0 0 0 0 0 0 0 0
5 test0 0x0 1004 1 0 0 1760 176 0 0 0 0 0 0 0 0 0 0 0 0
6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,5 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test_nss_tun0 0x0 1002 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
4 test0 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,4 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 500 50 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test0 0x0 1004 0 330 30 660 60 0 0 0 0 0 0 0 0 0 0 0 0
4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,4 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test0 0x0 1004 0 600 60 600 60 0 0 0 0 0 0 0 0 0 0 0 0
4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -0,0 +1,8 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
4 v4-test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
5 v4-test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
6 test0 0x0 0 0 9300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
7 test0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0

View File

@@ -2,5 +2,4 @@ idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packe
2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0 2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0 4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
5 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
6 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0