diff options
9 files changed, 183 insertions, 806 deletions
| diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index b27b1c057807..9cf582bef1d4 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -18,7 +18,6 @@ package android.net;  import static android.os.Process.CLAT_UID; -import android.annotation.NonNull;  import android.annotation.UnsupportedAppUsage;  import android.os.Parcel;  import android.os.Parcelable; @@ -34,7 +33,6 @@ import libcore.util.EmptyArray;  import java.io.CharArrayWriter;  import java.io.PrintWriter;  import java.util.Arrays; -import java.util.function.Predicate;  import java.util.HashSet;  import java.util.Map;  import java.util.Objects; @@ -995,33 +993,23 @@ 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); -            if (predicate.test(entry)) { -                if (nextOutputEntry != i) { -                    setValues(nextOutputEntry, entry); -                } +            final boolean matches = +                    (limitUid == UID_ALL || limitUid == entry.uid) +                    && (limitTag == TAG_ALL || limitTag == entry.tag) +                    && (limitIfaces == INTERFACES_ALL +                            || ArrayUtils.contains(limitIfaces, entry.iface)); + +            if (matches) { +                setValues(nextOutputEntry, entry);                  nextOutputEntry++;              }          } +          size = nextOutputEntry;      } @@ -1187,221 +1175,133 @@ public class NetworkStats implements Parcelable {      /**       * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.       * -     * <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 change -     * over time. +     * 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 +     * change 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 tunIface iface of the vpn tunnel -     * @param underlyingIfaces underlying network ifaces used by the VPN application +     * @param underlyingIface the primary underlying network iface used by the VPN application +     * @return true if it successfully adjusts the accounting for VPN, false otherwise       */ -    public void migrateTun(int tunUid, @NonNull String tunIface, -            @NonNull String[] underlyingIfaces) { -        // 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(); - -        for (int i = 0; i < perInterfaceTotal.length; i++) { -            perInterfaceTotal[i] = new Entry(); -        } +    public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) { +        Entry tunIfaceTotal = new Entry(); +        Entry underlyingIfaceTotal = new Entry(); -        tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal, -                underlyingIfacesTotal); +        tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal); -        // 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. +        // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app. +        // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression.          // Negative stats should be avoided. -        final Entry[] moved = -                addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, -                        perInterfaceTotal, underlyingIfacesTotal); -        deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved); +        Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal); +        if (pool.isEmpty()) { +            return true; +        } +        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.       * -     * <p>This is the first pass iteration which does the following work: -     * -     * <ul> -     *   <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} +     * 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). +     * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself.       */ -    private void tunAdjustmentInit(int tunUid, @NonNull String tunIface, -            @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal, -            @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) { -        final Entry recycle = new Entry(); +    private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface, +            Entry tunIfaceTotal, Entry underlyingIfaceTotal) { +        Entry recycle = new Entry();          for (int i = 0; i < size; i++) {              getValues(i, recycle);              if (recycle.uid == UID_ALL) {                  throw new IllegalStateException(                          "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(                          "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");              } -            if (recycle.tag != TAG_NONE) { -                // TODO(b/123666283): Take all tags for tunUid into account. -                continue; + +            if (recycle.uid == tunUid && recycle.tag == TAG_NONE +                    && Objects.equals(underlyingIface, recycle.iface)) { +                underlyingIfaceTotal.add(recycle);              } -            if (recycle.uid == tunUid) { -                // 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)) { +            if (recycle.uid != tunUid && recycle.tag == TAG_NONE +                    && Objects.equals(tunIface, recycle.iface)) {                  // Add up all tunIface traffic excluding traffic from the vpn app itself.                  tunIfaceTotal.add(recycle);              }          }      } -    /** -     * Distributes traffic across apps that are using given {@code tunIface}, and returns the total -     * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}. -     * -     * @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 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 static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) { +        Entry pool = new Entry(); +        pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes); +        pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets); +        pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes); +        pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets); +        pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations); +        return pool; +    } -        final Entry tmpEntry = new Entry(); -        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; -            } -            if (uid[i] == tunUid) { -                // Exclude VPN app from the redistribution, as it can choose to create packet -                // streams by writing to itself. -                continue; -            } -            tmpEntry.uid = uid[i]; -            tmpEntry.tag = tag[i]; -            tmpEntry.metered = metered[i]; -            tmpEntry.roaming = roaming[i]; -            tmpEntry.defaultNetwork = defaultNetwork[i]; - -            // 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) { -                // Note - The multiplication below should not overflow since NetworkStatsService -                // processes this every time device has transmitted/received amount equivalent to -                // global threshold alert (~ 2MB) across all interfaces. -                final long rxBytesAcrossUnderlyingIfaces = -                        underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; -                // app must not be blamed for more than it consumed on tunIface -                totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces); -            } -            long totalRxPackets = 0; -            if (tunIfaceTotal.rxPackets > 0) { -                final long rxPacketsAcrossUnderlyingIfaces = -                        underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; -                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]; -                if (underlyingIfacesTotal.rxBytes > 0) { -                    tmpEntry.rxBytes = -                            totalRxBytes -                                    * perInterfaceTotal[j].rxBytes -                                    / underlyingIfacesTotal.rxBytes; +    private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface, +            Entry tunIfaceTotal, Entry pool) { +        Entry moved = new Entry(); +        Entry tmpEntry = new Entry(); +        tmpEntry.iface = underlyingIface; +        for (int i = 0; i < size; i++) { +            // the vpn app is excluded from the redistribution but all moved traffic will be +            // deducted from the vpn app (see deductTrafficFromVpnApp below). +            if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) { +                if (tunIfaceTotal.rxBytes > 0) { +                    tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; +                } else { +                    tmpEntry.rxBytes = 0;                  } -                tmpEntry.rxPackets = 0; -                if (underlyingIfacesTotal.rxPackets > 0) { -                    tmpEntry.rxPackets = -                            totalRxPackets -                                    * perInterfaceTotal[j].rxPackets -                                    / underlyingIfacesTotal.rxPackets; +                if (tunIfaceTotal.rxPackets > 0) { +                    tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; +                } else { +                    tmpEntry.rxPackets = 0;                  } -                tmpEntry.txBytes = 0; -                if (underlyingIfacesTotal.txBytes > 0) { -                    tmpEntry.txBytes = -                            totalTxBytes -                                    * perInterfaceTotal[j].txBytes -                                    / underlyingIfacesTotal.txBytes; +                if (tunIfaceTotal.txBytes > 0) { +                    tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes; +                } else { +                    tmpEntry.txBytes = 0;                  } -                tmpEntry.txPackets = 0; -                if (underlyingIfacesTotal.txPackets > 0) { -                    tmpEntry.txPackets = -                            totalTxPackets -                                    * perInterfaceTotal[j].txPackets -                                    / underlyingIfacesTotal.txPackets; +                if (tunIfaceTotal.txPackets > 0) { +                    tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets; +                } else { +                    tmpEntry.txPackets = 0;                  } -                tmpEntry.operations = 0; -                if (underlyingIfacesTotal.operations > 0) { +                if (tunIfaceTotal.operations > 0) {                      tmpEntry.operations = -                            totalOperations -                                    * perInterfaceTotal[j].operations -                                    / underlyingIfacesTotal.operations; +                            pool.operations * operations[i] / tunIfaceTotal.operations; +                } else { +                    tmpEntry.operations = 0;                  } -                // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying -                // interface. Add that data usage to this object. +                tmpEntry.uid = uid[i]; +                tmpEntry.tag = tag[i]; +                tmpEntry.set = set[i]; +                tmpEntry.metered = metered[i]; +                tmpEntry.roaming = roaming[i]; +                tmpEntry.defaultNetwork = defaultNetwork[i];                  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); +                    moved.add(tmpEntry);                      // Add debug info                      tmpEntry.set = SET_DBG_VPN_IN;                      combineValues(tmpEntry); @@ -1411,45 +1311,38 @@ public class NetworkStats implements Parcelable {          return moved;      } -    private void deductTrafficFromVpnApp( -            int tunUid, -            @NonNull String[] underlyingIfaces, -            @NonNull Entry[] moved) { -        for (int i = 0; i < underlyingIfaces.length; i++) { -            moved[i].uid = tunUid; -            // Add debug info -            moved[i].set = SET_DBG_VPN_OUT; -            moved[i].tag = TAG_NONE; -            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 -            // the TAG_NONE traffic. -            // -            // Relies on the fact that the underlying traffic only has state ROAMING_NO and -            // METERED_NO, which should be the case as it comes directly from the /proc file. -            // We only blend in the roaming data after applying these adjustments, by checking the -            // NetworkIdentity of the underlying iface. -            final int idxVpnBackground = findIndex(underlyingIfaces[i], tunUid, SET_DEFAULT, -                            TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); -            if (idxVpnBackground != -1) { -                // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed -                // from foreground usage. -                tunSubtract(idxVpnBackground, this, moved[i]); -            } +    private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) { +        // Add debug info +        moved.uid = tunUid; +        moved.set = SET_DBG_VPN_OUT; +        moved.tag = TAG_NONE; +        moved.iface = underlyingIface; +        moved.metered = METERED_ALL; +        moved.roaming = ROAMING_ALL; +        moved.defaultNetwork = DEFAULT_NETWORK_ALL; +        combineValues(moved); + +        // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than +        // the TAG_NONE traffic. +        // +        // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO, +        // which should be the case as it comes directly from the /proc file. We only blend in the +        // roaming data after applying these adjustments, by checking the NetworkIdentity of the +        // underlying iface. +        int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, +                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); +        if (idxVpnBackground != -1) { +            tunSubtract(idxVpnBackground, this, moved); +        } -            final int idxVpnForeground = findIndex(underlyingIfaces[i], tunUid, SET_FOREGROUND, -                            TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); -            if (idxVpnForeground != -1) { -                tunSubtract(idxVpnForeground, this, moved[i]); -            } +        int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, +                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); +        if (idxVpnForeground != -1) { +            tunSubtract(idxVpnForeground, this, moved);          }      } -    private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) { +    private static void tunSubtract(int i, NetworkStats left, Entry right) {          long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);          left.rxBytes[i] -= rxBytes;          right.rxBytes -= rxBytes; diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java index e74af5eb50de..b1a412871bd2 100644 --- a/core/java/com/android/internal/net/VpnInfo.java +++ b/core/java/com/android/internal/net/VpnInfo.java @@ -19,8 +19,6 @@ package com.android.internal.net;  import android.os.Parcel;  import android.os.Parcelable; -import java.util.Arrays; -  /**   * A lightweight container used to carry information of the ongoing VPN.   * Internal use only.. @@ -30,14 +28,14 @@ import java.util.Arrays;  public class VpnInfo implements Parcelable {      public int ownerUid;      public String vpnIface; -    public String[] underlyingIfaces; +    public String primaryUnderlyingIface;      @Override      public String toString() {          return "VpnInfo{"                  + "ownerUid=" + ownerUid                  + ", vpnIface='" + vpnIface + '\'' -                + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\'' +                + ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\''                  + '}';      } @@ -50,7 +48,7 @@ public class VpnInfo implements Parcelable {      public void writeToParcel(Parcel dest, int flags) {          dest.writeInt(ownerUid);          dest.writeString(vpnIface); -        dest.writeStringArray(underlyingIfaces); +        dest.writeString(primaryUnderlyingIface);      }      public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() { @@ -59,7 +57,7 @@ public class VpnInfo implements Parcelable {              VpnInfo info = new VpnInfo();              info.ownerUid = source.readInt();              info.vpnIface = source.readString(); -            info.underlyingIfaces = source.readStringArray(); +            info.primaryUnderlyingIface = source.readString();              return info;          } diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java index 707d7b30e09b..1b6560322a13 100644 --- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java +++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java @@ -19,22 +19,13 @@ package android.net;  import com.google.caliper.BeforeExperiment;  import com.google.caliper.Param; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -  public class NetworkStatsBenchmark { -    private static final String[] UNDERLYING_IFACES = {"wlan0", "rmnet0"}; +    private static final String UNDERLYING_IFACE = "wlan0";      private static final String TUN_IFACE = "tun0";      private static final int TUN_UID = 999999999;      @Param({"100", "1000"})      private int mSize; -    /** -     * Should not be more than the length of {@link #UNDERLYING_IFACES}. -     */ -    @Param({"1", "2"}) -    private int mNumUnderlyingIfaces;      private NetworkStats mNetworkStats;      @BeforeExperiment @@ -42,10 +33,8 @@ public class NetworkStatsBenchmark {          mNetworkStats = new NetworkStats(0, mSize + 2);          int uid = 0;          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++) { -            recycle.iface = allIfaces.get(i % totalIfaces); +            recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE;              recycle.uid = uid;              recycle.set = i % 2;              recycle.tag = NetworkStats.TAG_NONE; @@ -59,39 +48,22 @@ public class NetworkStatsBenchmark {                  uid++;              }          } - -        for (int i = 0; i < mNumUnderlyingIfaces; i++) { -            recycle.iface = UNDERLYING_IFACES[i]; -            recycle.uid = TUN_UID; -            recycle.set = NetworkStats.SET_FOREGROUND; -            recycle.tag = NetworkStats.TAG_NONE; -            recycle.rxBytes = 90000 * mSize; -            recycle.rxPackets = 40 * mSize; -            recycle.txBytes = 180000 * mSize; -            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; +        recycle.iface = UNDERLYING_IFACE; +        recycle.uid = TUN_UID; +        recycle.set = NetworkStats.SET_FOREGROUND; +        recycle.tag = NetworkStats.TAG_NONE; +        recycle.rxBytes = 90000 * mSize; +        recycle.rxPackets = 40 * mSize; +        recycle.txBytes = 180000 * mSize; +        recycle.txPackets = 1200 * mSize; +        recycle.operations = 0; +        mNetworkStats.addValues(recycle);      }      public void timeMigrateTun(int reps) {          for (int i = 0; i < reps; i++) {              NetworkStats stats = mNetworkStats.clone(); -            stats.migrateTun(TUN_UID, TUN_IFACE, getVpnUnderlyingIfaces()); +            stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE);          }      } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 07c9dc1cde44..f6a972b0906f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -966,6 +966,8 @@ public class ConnectivityService extends IConnectivityManager.Stub              }          } +        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); +          mTethering = makeTethering();          mPermissionMonitor = new PermissionMonitor(mContext, mNetd); @@ -1013,8 +1015,6 @@ public class ConnectivityService extends IConnectivityManager.Stub          final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext);          dataConnectionStats.startMonitoring(); -        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); -          mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);          mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,                  mContext.getSystemService(NotificationManager.class)); @@ -4374,7 +4374,7 @@ public class ConnectivityService extends IConnectivityManager.Stub      /**       * @return VPN information for accounting, or null if we can't retrieve all required -     *         information, e.g underlying ifaces. +     *         information, e.g primary underlying iface.       */      @Nullable      private VpnInfo createVpnInfo(Vpn vpn) { @@ -4386,24 +4386,17 @@ public class ConnectivityService extends IConnectivityManager.Stub          // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret          // the underlyingNetworks list.          if (underlyingNetworks == null) { -            NetworkAgentInfo defaultNai = getDefaultNetwork(); -            if (defaultNai != null) { -                underlyingNetworks = new Network[] { defaultNai.network }; -            } -        } -        if (underlyingNetworks != null && underlyingNetworks.length > 0) { -            List<String> interfaces = new ArrayList<>(); -            for (Network network : underlyingNetworks) { -                LinkProperties lp = getLinkProperties(network); -                if (lp != null && !TextUtils.isEmpty(lp.getInterfaceName())) { -                    interfaces.add(lp.getInterfaceName()); -                } +            NetworkAgentInfo defaultNetwork = getDefaultNetwork(); +            if (defaultNetwork != null && defaultNetwork.linkProperties != null) { +                info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();              } -            if (!interfaces.isEmpty()) { -                info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]); +        } else if (underlyingNetworks.length > 0) { +            LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]); +            if (linkProperties != null) { +                info.primaryUnderlyingIface = linkProperties.getInterfaceName();              }          } -        return info.underlyingIfaces == null ? null : info; +        return info.primaryUnderlyingIface == null ? null : info;      }      /** diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 2e64965a6181..69efd02dea9c 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -263,11 +263,6 @@ public class NetworkStatsFactory {          return stats;      } -    /** -     * @deprecated Use NetworkStatsService#getDetailedUidStats which also accounts for -     * VPN traffic -     */ -    @Deprecated      public NetworkStats readNetworkStatsDetail() throws IOException {          return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);      } diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java index bdff50053fae..a2e7e0cae96b 100644 --- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java @@ -41,10 +41,10 @@ import com.android.internal.net.VpnInfo;  import com.android.internal.util.FileRotator;  import com.android.internal.util.IndentingPrintWriter; -import com.google.android.collect.Sets; -  import libcore.io.IoUtils; +import com.google.android.collect.Sets; +  import java.io.ByteArrayOutputStream;  import java.io.DataOutputStream;  import java.io.File; @@ -234,7 +234,7 @@ public class NetworkStatsRecorder {          if (vpnArray != null) {              for (VpnInfo info : vpnArray) { -                delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); +                delta.migrateTun(info.ownerUid, info.vpnIface, info.primaryUnderlyingIface);              }          } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 484efd6bfd74..f9972dd87368 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -292,22 +292,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {      /** Data layer operation counters for splicing into other structures. */      private NetworkStats mUidOperations = new NetworkStats(0L, 10); -    /** -     * Snapshot containing most recent network stats for all UIDs across all interfaces and tags -     * since boot. -     * -     * <p>Maintains migrated VPN stats which are result of performing TUN migration on {@link -     * #mLastUidDetailSnapshot}. -     */ -    @GuardedBy("mStatsLock") -    private NetworkStats mTunAdjustedStats; -    /** -     * Used by {@link #mTunAdjustedStats} to migrate VPN traffic over delta between this snapshot -     * and latest snapshot. -     */ -    @GuardedBy("mStatsLock") -    private NetworkStats mLastUidDetailSnapshot; -      /** Must be set in factory by calling #setHandler. */      private Handler mHandler;      private Handler.Callback mHandlerCallback; @@ -821,39 +805,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {      @Override      public NetworkStats getDetailedUidStats(String[] requiredIfaces) {          try { -            // Get the latest snapshot from NetworkStatsFactory. -            // TODO: Querying for INTERFACES_ALL may incur performance penalty. Consider restricting -            // this to limited set of ifaces. -            NetworkStats uidDetailStats = getNetworkStatsUidDetail(INTERFACES_ALL); - -            // Migrate traffic from VPN UID over delta and update mTunAdjustedStats. -            NetworkStats result; -            synchronized (mStatsLock) { -                migrateTunTraffic(uidDetailStats, mVpnInfos); -                result = mTunAdjustedStats.clone(); -            } - -            // Apply filter based on ifacesToQuery.              final String[] ifacesToQuery =                      NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces); -            result.filter(UID_ALL, ifacesToQuery, TAG_ALL); -            return result; +            return getNetworkStatsUidDetail(ifacesToQuery);          } catch (RemoteException e) {              Log.wtf(TAG, "Error compiling UID stats", e);              return new NetworkStats(0L, 0);          }      } -    @VisibleForTesting -    NetworkStats getTunAdjustedStats() { -        synchronized (mStatsLock) { -            if (mTunAdjustedStats == null) { -                return null; -            } -            return mTunAdjustedStats.clone(); -        } -    } -      @Override      public String[] getMobileIfaces() {          return mMobileIfaces; @@ -1328,34 +1288,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {          // a race condition between the service handler thread and the observer's          mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces),                  new ArrayMap<>(mActiveUidIfaces), vpnArray, currentTime); - -        migrateTunTraffic(uidSnapshot, vpnArray); -    } - -    /** -     * Updates {@link #mTunAdjustedStats} with the delta containing traffic migrated off of VPNs. -     */ -    @GuardedBy("mStatsLock") -    private void migrateTunTraffic(NetworkStats uidDetailStats, VpnInfo[] vpnInfoArray) { -        if (mTunAdjustedStats == null) { -            // Either device booted or system server restarted, hence traffic cannot be migrated -            // correctly without knowing the past state of VPN's underlying networks. -            mTunAdjustedStats = uidDetailStats; -            mLastUidDetailSnapshot = uidDetailStats; -            return; -        } -        // Migrate delta traffic from VPN to other apps. -        NetworkStats delta = uidDetailStats.subtract(mLastUidDetailSnapshot); -        for (VpnInfo info : vpnInfoArray) { -            delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); -        } -        // Filter out debug entries as that may lead to over counting. -        delta.filterDebugEntries(); -        // Update #mTunAdjustedStats with migrated delta. -        mTunAdjustedStats.combineAllValues(delta); -        mTunAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime()); -        // Update last snapshot. -        mLastUidDetailSnapshot = uidDetailStats;      }      /** diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java index c16a0f446651..b5b0384ca599 100644 --- a/tests/net/java/android/net/NetworkStatsTest.java +++ b/tests/net/java/android/net/NetworkStatsTest.java @@ -569,7 +569,7 @@ public class NetworkStatsTest {              .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,                      DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); -        delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface}); +        assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface));          assertEquals(20, delta.size());          // 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,                      DEFAULT_NETWORK_NO,  75500L, 37L, 130000L, 70L, 0L); -        delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface}); +        assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));          assertEquals(9, delta.size());          // tunIface entries should not be changed. @@ -813,37 +813,6 @@ public class NetworkStatsTest {      }      @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      public void testApply464xlatAdjustments() {          final String v4Iface = "v4-wlan0";          final String baseIface = "wlan0"; diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 31df9f3bc9c5..f453f6e6c415 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -56,11 +56,11 @@ import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_PO  import static org.junit.Assert.assertEquals;  import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull;  import static org.junit.Assert.assertTrue;  import static org.mockito.ArgumentMatchers.any;  import static org.mockito.ArgumentMatchers.anyInt;  import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat;  import static org.mockito.ArgumentMatchers.eq;  import static org.mockito.Mockito.times;  import static org.mockito.Mockito.verify; @@ -216,16 +216,11 @@ public class NetworkStatsServiceTest {          expectNetworkStatsUidDetail(buildEmptyStats());          expectSystemReady(); -        assertNull(mService.getTunAdjustedStats());          mService.systemReady(); -        // Verify that system ready fetches realtime stats and initializes tun adjusted stats. -        verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); -        assertNotNull("failed to initialize TUN adjusted stats", mService.getTunAdjustedStats()); -        assertEquals(0, mService.getTunAdjustedStats().size()); -          mSession = mService.openSession();          assertNotNull("openSession() failed", mSession); +          // catch INetworkManagementEventObserver during systemReady()          ArgumentCaptor<INetworkManagementEventObserver> networkObserver =                ArgumentCaptor.forClass(INetworkManagementEventObserver.class); @@ -738,13 +733,11 @@ public class NetworkStatsServiceTest {          NetworkStats stats = mService.getDetailedUidStats(ifaceFilter); -        // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations: -        // 1) NetworkStatsService#systemReady from #setUp. -        // 2) mService#forceUpdateIfaces in the test above. -        // 3) Finally, mService#getDetailedUidStats. -        verify(mNetManager, times(3)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); -        assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE)); -        assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface)); +        verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces -> +                ifaces != null && ifaces.length == 2 +                        && ArrayUtils.contains(ifaces, TEST_IFACE) +                        && ArrayUtils.contains(ifaces, stackedIface))); +          assertEquals(2, stats.size());          assertEquals(uidStats, stats.getValues(0, null));          assertEquals(tetheredStats1, stats.getValues(1, null)); @@ -930,111 +923,11 @@ public class NetworkStatsServiceTest {      }      @Test -    public void vpnRewriteTrafficThroughItself() 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(new String[] {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, 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 -        // (100 bytes). -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 5) -                .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L) -                .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L) -                // VPN rewrites all the packets read from TUN + 100 additional bytes of VPN's -                // own traffic. -                .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 0L, 0L, 1600L, 160L, 2L) -                // VPN sent 1760 bytes over WiFi in foreground (SET_FOREGROUND) i.e. 1600 -                // bytes (160 packets) + 1 byte/packet overhead (=160 bytes). -                .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1760L, 176L, 1L) -                // VPN received 3300 bytes over WiFi in background (SET_DEFAULT) i.e. 3000 bytes -                // (300 packets) + 1 byte/packet encryption overhead (=300 bytes). -                .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L)); - -        forcePollAndWaitForIdle(); - -        // Verify increased TUN usage by UID_VPN does not get attributed to other apps. -        NetworkStats tunStats = -                mService.getDetailedUidStats(new String[] {TUN_IFACE}); -        assertValues( -                tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 1); -        assertValues( -                tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 1); -        assertValues( -                tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 2); - -        // Verify correct attribution over WiFi. -        assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1); -        assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1); -        assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 260L, 26L, 2); -    } - -    @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(new String[] {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, 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. -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) -                .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L) -                .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L) -                // VPN received 3300 bytes over WiFi in background (SET_DEFAULT). -                .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L) -                // VPN sent 1650 bytes over WiFi in foreground (SET_FOREGROUND). -                .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1650L, 150L, 1L)); - -        forcePollAndWaitForIdle(); - -        assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1); -        assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1); -        assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 150L, 0L, 2); -    } - -    @Test -    public void vpnWithOneUnderlyingIfaceAndOwnTraffic() 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(new String[] {TEST_IFACE})}; +        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};          expectNetworkStatsUidDetail(buildEmptyStats());          expectBandwidthControlCheck(); @@ -1045,33 +938,23 @@ public class NetworkStatsServiceTest {                  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, 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. +        // 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, 2000L, 200L, 1000L, 100L, 1L) -                .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L) -                .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 5000L, 500L, 6000L, 600L, 1L) -                // VPN received 8800 bytes over WiFi in background (SET_DEFAULT). -                .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 8800L, 800L, 0L, 0L, 1L) -                // VPN sent 8250 bytes over WiFi in foreground (SET_FOREGROUND). -                .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 8250L, 750L, 1L)); +                .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, 2000L, 200L, 1000L, 100L, 1); -        assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1); -        assertUidTotal(sTemplateWifi, UID_VPN, 5800L, 500L, 6750L, 600L, 2); +        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 @@ -1079,7 +962,7 @@ public class NetworkStatsServiceTest {          // 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(new String[] {TEST_IFACE})}; +        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};          expectNetworkStatsUidDetail(buildEmptyStats());          expectBandwidthControlCheck(); @@ -1110,136 +993,6 @@ public class NetworkStatsServiceTest {      }      @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. -        expectDefaultSettings(); -        NetworkState[] networkStates = -                new NetworkState[] { -                    buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() -                }; -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; -        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 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. -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) -                .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) -                .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) -                .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L) -                .addValues( -                    TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L)); - -        forcePollAndWaitForIdle(); - -        assertUidTotal(sTemplateWifi, UID_RED, 500L, 50L, 500L, 50L, 1); -        assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1); -        assertUidTotal(sTemplateWifi, UID_VPN, 1200L, 100L, 1200L, 100L, 2); - -        assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 500L, 50L, 500L, 50L, 1); -        assertUidTotal(buildTemplateMobileWildcard(), UID_BLUE, 500L, 50L, 500L, 50L, 1); -        assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1200L, 100L, 1200L, 100L, 2); -    } - -    @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. -        expectDefaultSettings(); -        NetworkState[] networkStates = -                new NetworkState[] { -                    buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() -                }; -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; -        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, 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. -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) -              .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 500L, 50L, 1000L, 100L, 2L) -              .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 330L, 30L, 660L, 60L, 1L) -              .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 220L, 20L, 440L, 40L, 1L)); - -        forcePollAndWaitForIdle(); - -        assertUidTotal(sTemplateWifi, UID_RED, 300L, 30L, 600L, 60L, 1); -        assertUidTotal(sTemplateWifi, UID_VPN, 30L, 0L, 60L, 0L, 1); - -        assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 400L, 40L, 1); -        assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 20L, 0L, 40L, 0L, 1); -    } - -    @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. -        expectDefaultSettings(); -        NetworkState[] networkStates = -                new NetworkState[] { -                    buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() -                }; -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; -        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: -        // 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). -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) -              .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) -              .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 600L, 60L, 600L, 60L, 0L) -              .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 200L, 20L, 200L, 20L, 0L)); - -        forcePollAndWaitForIdle(); - -        assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 0); -        assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0); - -        assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 200L, 20L, 0); -        assertUidTotal(buildTemplateMobileWildcard(), 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. @@ -1248,7 +1001,7 @@ public class NetworkStatsServiceTest {                  new NetworkState[] {                      buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()                  }; -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; +        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};          expectNetworkStatsUidDetail(buildEmptyStats());          expectBandwidthControlCheck(); @@ -1277,134 +1030,6 @@ public class NetworkStatsServiceTest {      }      @Test -    public void recordSnapshot_migratesTunTrafficAndUpdatesTunAdjustedStats() throws Exception { -        assertEquals(0, mService.getTunAdjustedStats().size()); -        // VPN using WiFi (TEST_IFACE). -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; -        expectBandwidthControlCheck(); -        // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption -        // overhead per packet): -        // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. -        // VPN received 1100 bytes (100 packets) over WiFi. -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) -              .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) -              .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - -        // this should lead to NSS#recordSnapshotLocked -        mService.forceUpdateIfaces( -                new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - -        // Verify TUN adjusted stats have traffic migrated correctly. -        // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100 -        // bytes attributed to UID_VPN. -        NetworkStats tunAdjStats = mService.getTunAdjustedStats(); -        assertValues( -                tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); -        assertValues( -                tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); -    } - -    @Test -    public void getDetailedUidStats_migratesTunTrafficAndUpdatesTunAdjustedStats() -            throws Exception { -        assertEquals(0, mService.getTunAdjustedStats().size()); -        // VPN using WiFi (TEST_IFACE). -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; -        expectBandwidthControlCheck(); -        mService.forceUpdateIfaces( -                new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); -        // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption -        // overhead per packet): -        // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. -        // VPN received 1100 bytes (100 packets) over WiFi. -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) -              .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) -              .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - -        mService.getDetailedUidStats(INTERFACES_ALL); - -        // Verify internally maintained TUN adjusted stats -        NetworkStats tunAdjStats = mService.getTunAdjustedStats(); -        // Verify stats for TEST_IFACE (WiFi): -        // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100 -        // bytes attributed to UID_VPN. -        assertValues( -                tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); -        assertValues( -                tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); -        // Verify stats for TUN_IFACE; only UID_RED should have usage on it. -        assertValues( -                tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); -        assertValues( -                tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0); - -        // lets assume that since last time, VPN received another 1100 bytes (same assumptions as -        // before i.e. UID_RED downloaded another 1000 bytes). -        incrementCurrentTime(HOUR_IN_MILLIS); -        // Note - NetworkStatsFactory returns counters that are monotonically increasing. -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) -              .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 0L, 0L, 0L) -              .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 0L, 0L, 0L)); - -        mService.getDetailedUidStats(INTERFACES_ALL); - -        tunAdjStats = mService.getTunAdjustedStats(); -        // verify TEST_IFACE stats: -        assertValues( -                tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0); -        assertValues( -                tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 200L, 0L, 0L, 0L, 0); -        // verify TUN_IFACE stats: -        assertValues( -                tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0); -        assertValues( -                tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0); -    } - -    @Test -    public void getDetailedUidStats_returnsCorrectStatsWithVpnRunning() throws Exception { -        // VPN using WiFi (TEST_IFACE). -        VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; -        expectBandwidthControlCheck(); -        mService.forceUpdateIfaces( -                new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); -        // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption -        // overhead per packet): -        // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. -        // VPN received 1100 bytes (100 packets) over WiFi. -        incrementCurrentTime(HOUR_IN_MILLIS); -        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) -              .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) -              .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - -        // Query realtime stats for TEST_IFACE. -        NetworkStats queriedStats = -                mService.getDetailedUidStats(new String[] {TEST_IFACE}); - -        assertEquals(HOUR_IN_MILLIS, queriedStats.getElapsedRealtime()); -        // verify that returned stats are only for TEST_IFACE and VPN traffic is migrated correctly. -        assertEquals(new String[] {TEST_IFACE}, queriedStats.getUniqueIfaces()); -        assertValues( -                queriedStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); -        assertValues( -                queriedStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, -                DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); -    } - -    @Test      public void testRegisterUsageCallback() throws Exception {          // pretend that wifi network comes online; service should ask about full          // network state, and poll any existing interfaces before updating. @@ -1752,11 +1377,11 @@ public class NetworkStatsServiceTest {          return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);      } -    private static VpnInfo createVpnInfo(String[] underlyingIfaces) { +    private static VpnInfo createVpnInfo(String underlyingIface) {          VpnInfo info = new VpnInfo();          info.ownerUid = UID_VPN;          info.vpnIface = TUN_IFACE; -        info.underlyingIfaces = underlyingIfaces; +        info.primaryUnderlyingIface = underlyingIface;          return info;      } |