Merge "VPN network stat accounting changes."
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 25806fa..f65a50f 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -904,7 +904,8 @@
if (pool.isEmpty()) {
return true;
}
- Entry moved = addTrafficToApplications(tunIface, underlyingIface, tunIfaceTotal, pool);
+ Entry moved =
+ addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
if (!moved.isEmpty()) {
@@ -919,9 +920,9 @@
* Initializes the data used by the migrateTun() method.
*
* This is the first pass iteration which does the following work:
- * (1) Adds up all the traffic through tun0.
- * (2) Adds up all the traffic through the tunUid's underlyingIface
+ * (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, String tunIface, String underlyingIface,
Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
@@ -941,8 +942,9 @@
underlyingIfaceTotal.add(recycle);
}
- if (recycle.tag == TAG_NONE && Objects.equals(tunIface, recycle.iface)) {
- // Add up all tunIface traffic.
+ 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);
}
}
@@ -958,13 +960,15 @@
return pool;
}
- private Entry addTrafficToApplications(String tunIface, String underlyingIface,
+ 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++) {
- if (Objects.equals(iface[i], tunIface)) {
+ // 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 {
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 9074f8a..d48a67a 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -454,7 +454,7 @@
.addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
- assertEquals(21, delta.size());
+ assertEquals(20, delta.size());
// tunIface and TEST_IFACE entries are not changed.
assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
@@ -478,38 +478,89 @@
// Existing underlying Iface entries are updated
assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
- 44783L, 54L, 13829L, 60L, 0L);
+ 44783L, 54L, 14178L, 62L, 0L);
assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
0L, 0L, 0L, 0L, 0L);
// VPN underlying Iface entries are updated
assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
- 28304L, 27L, 1719L, 12L, 0L);
+ 28304L, 27L, 1L, 2L, 0L);
assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
0L, 0L, 0L, 0L, 0L);
// New entries are added for new application's underlying Iface traffic
assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_NO,
- 72667L, 197L, 41872L, 219L, 0L);
+ 72667L, 197L, 43123L, 227L, 0L);
assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
- 9297L, 17L, 3936, 19L, 0L);
+ 9297L, 17L, 4054, 19L, 0L);
assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, ROAMING_NO,
- 21691L, 41L, 13179L, 46L, 0L);
+ 21691L, 41L, 13572L, 48L, 0L);
assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, ROAMING_NO,
- 1281L, 2L, 634L, 1L, 0L);
+ 1281L, 2L, 653L, 1L, 0L);
// New entries are added for debug purpose
assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
- 39605L, 46L, 11690, 49, 0);
+ 39605L, 46L, 12039, 51, 0);
assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
- 81964, 214, 45808, 238, 0);
- assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
- 4983, 10, 1717, 10, 0);
+ 81964, 214, 47177, 246, 0);
assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL,
- 126552, 270, 59215, 297, 0);
+ 121569, 260, 59216, 297, 0);
}
+ // Tests a case where all of the data received by the tun0 interface is echo back into the tun0
+ // interface by the vpn app before it's sent out of the underlying interface. The VPN app should
+ // not be charged for the echoed data but it should still be charged for any extra data it sends
+ // via the underlying interface.
+ public void testMigrateTun_VpnAsLoopback() {
+ final int tunUid = 10030;
+ final String tunIface = "tun0";
+ final String underlyingIface = "wlan0";
+ NetworkStats delta = new NetworkStats(TEST_START, 9)
+ // 2 different apps sent/receive data via tun0.
+ .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, 50000L, 25L, 100000L, 50L, 0L)
+ .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, 500L, 2L, 200L, 5L, 0L)
+ // VPN package resends data through the tunnel (with exaggerated overhead)
+ .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, 240000, 100L, 120000L, 60L, 0L)
+ // 1 app already has some traffic on the underlying interface, the other doesn't yet
+ .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, 1000L, 10L, 2000L, 20L, 0L)
+ // Traffic through the underlying interface via the vpn app.
+ // This test should redistribute this data correctly.
+ .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
+ 75500L, 37L, 130000L, 70L, 0L);
+
+ assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+ assertEquals(9, delta.size());
+
+ // tunIface entries should not be changed.
+ assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+ 50000L, 25L, 100000L, 50L, 0L);
+ assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+ 500L, 2L, 200L, 5L, 0L);
+ assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+ 240000L, 100L, 120000L, 60L, 0L);
+
+ // Existing underlying Iface entries are updated
+ assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+ 51000L, 35L, 102000L, 70L, 0L);
+
+ // VPN underlying Iface entries are updated
+ assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+ 25000L, 10L, 29800L, 15L, 0L);
+
+ // New entries are added for new application's underlying Iface traffic
+ assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
+ 500L, 2L, 200L, 5L, 0L);
+
+ // New entries are added for debug purpose
+ assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
+ 50000L, 25L, 100000L, 50L, 0L);
+ assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
+ 500, 2L, 200L, 5L, 0L);
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL,
+ 50500L, 27L, 100200L, 55, 0);
+ }
+
private static void assertContains(NetworkStats stats, String iface, int uid, int set,
int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
long operations) {