NetworkStatsFactory: Take VPNs into account for network/battery stats

This change fixes detailed UID stats to ensure network and battery stats
both take VPNs into account. NetworkStatsFactory is being made aware of
VPNs enabled, and the full set of underlying networks present.

Since traffic can only be migrated over a NetworkStats delta, NSF
maintains a NetworkStats snapshot across all UIDs/ifaces/tags.

This snapshot gets updated whenever NSF records a new snapshot
(based on various hooks such as VPN updating its underlying networks,
network getting lost, etc.), or NetworkStatsService's
getDetailedUidStats() method being called.

This change widens the scope of the existing mPersistentSnapshot lock,
renaming it to mPersistentDataLock, and ensures that TUN migrations are
not done in parallel. Additionally, mVpnInfos is updated via
pointer-swapping, to reduce the scope of the mPersistentDataLock.

The safety of this change is predicated on:
1. NetworkStatsFactory lock not held, so services cannot deadlock through
the cyclical lock.

2. The broadening of the scope of the lock in NetworkStatsFactory has no
threading implications, as it is always the last (leaf node) lock held,
and therefore is impossible to have lock inversion.

Additionally, to ensure VPNs work with 464xlat, the VPN info passed to
the NetworkStatsFactory includes all underlying interfaces, instead of
only passing the first one.

This (partially) re-applies changes from:
aosp/972848: Add one more test for VPN usage stats.
aosp/972847: Addressing comments for http://ag/7700679.
aosp/885338: NetworkStatsService: Fix getDetailedUidStats to take VPNs
             into account.
Co-developed with: Varun Anand <vaanand@google.com>

Bug: 113122541
Bug: 120145746
Bug: 129264869
Bug: 134244752
Test: FrameworksNetTest passing
Test: Manual tests show data usage fixes maintained.
Change-Id: I6466ec1411fc5ed6954125d27d353b6cd1be719e
This commit is contained in:
Benedict Wong
2019-06-12 17:46:31 +00:00
parent 9491d3a253
commit 44f698e1c7
4 changed files with 140 additions and 68 deletions

View File

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

View File

@@ -23,7 +23,6 @@ import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -37,6 +36,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
/**
* Collection of active network statistics. Can contain summary details across
@@ -994,23 +994,33 @@ public class NetworkStats implements Parcelable {
if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
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();
int nextOutputEntry = 0;
for (int i = 0; i < size; i++) {
entry = getValues(i, entry);
final boolean matches =
(limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)
&& (limitIfaces == INTERFACES_ALL
|| ArrayUtils.contains(limitIfaces, entry.iface));
if (matches) {
setValues(nextOutputEntry, entry);
if (predicate.test(entry)) {
if (nextOutputEntry != i) {
setValues(nextOutputEntry, entry);
}
nextOutputEntry++;
}
}
size = nextOutputEntry;
}
@@ -1289,7 +1299,8 @@ public class NetworkStats implements Parcelable {
}
final Entry tmpEntry = new Entry();
for (int i = 0; i < size; i++) {
final int origSize = size;
for (int i = 0; i < origSize; i++) {
if (!Objects.equals(iface[i], tunIface)) {
// Consider only entries that go onto the VPN interface.
continue;
@@ -1305,8 +1316,9 @@ public class NetworkStats implements Parcelable {
tmpEntry.roaming = roaming[i];
tmpEntry.defaultNetwork = defaultNetwork[i];
// In a first pass, compute each UID's total share of data across all underlyingIfaces.
// This is computed on the basis of the share of each UID's usage over tunIface.
// In a first pass, compute this entry's total share of data across all
// underlyingIfaces. This is computed on the basis of the share of this entry's usage
// over tunIface.
// TODO: Consider refactoring first pass into a separate helper method.
long totalRxBytes = 0;
if (tunIfaceTotal.rxBytes > 0) {
@@ -1383,9 +1395,11 @@ public class NetworkStats implements Parcelable {
* 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);
if (tag[i] == TAG_NONE) {
// Add the migrated data to moved so it is deducted from the VPN app later.
moved[j].add(tmpEntry);
// Add debug info
tmpEntry.set = SET_DBG_VPN_IN;
@@ -1401,8 +1415,8 @@ public class NetworkStats implements Parcelable {
@NonNull String[] underlyingIfaces,
@NonNull Entry[] moved) {
for (int i = 0; i < underlyingIfaces.length; i++) {
// Add debug info
moved[i].uid = tunUid;
// Add debug info
moved[i].set = SET_DBG_VPN_OUT;
moved[i].tag = TAG_NONE;
moved[i].iface = underlyingIfaces[i];