Add tether offload traffic to interface stats as well.

Currently, we only count add tethering traffic to per-UID
stats, but not to total data usage (i.e., dev and XT stats). This
is correct for software tethering, because all software forwarded
packets are already included in interface counters, but it is
incorrect for hardware offload, because such packets do not
increment interface counters.

To fix this:
1. Add an argument to ITetheringStatsProvider#getTetherStats to
   indicate whether per-UID stats are requested. For clarity,
   define integer constants STATS_PER_IFACE and STATS_PER_UID
   to represent these operations.
2. Make NetdTetheringStatsProvider return stats only if per-UID
   stats are requested. (Otherwise tethering traffic would be
   double-counted).
3. Make OffloadController's stats provider return the same
   stats regardless of whether per-UID stats were requested or
   not.
4. Make NetworkStatsService add non-per-UID tethering stats to
   the dev and XT snapshots. The per-UID snapshots were already
   correctly adding in per-UID stats.

(cherry picked from commit 5356a35c3b)

Bug: 29337859
Bug: 32163131
Test: runtest frameworks-net
Test: runtest frameworks-telephony
Change-Id: I325b13d50e88841dfb0db4c35e7e27f163ee72fe
Merged-In: I4e8e923d68dce1a4a68608dbd6c75a91165aa4ee
This commit is contained in:
Lorenzo Colitti
2017-08-17 19:23:08 +09:00
parent 9f0baa94ca
commit f1912ca49a
9 changed files with 89 additions and 27 deletions

View File

@@ -30,7 +30,10 @@ import android.net.NetworkStats;
*/
interface ITetheringStatsProvider {
// Returns cumulative statistics for all tethering sessions since boot, on all upstreams.
NetworkStats getTetherStats();
// @code {how} is one of the NetworkStats.STATS_PER_* constants. If {@code how} is
// {@code STATS_PER_IFACE}, the provider should not include any traffic that is already
// counted by kernel interface counters.
NetworkStats getTetherStats(int how);
// Sets the interface quota for the specified upstream interface. This is defined as the number
// of bytes, starting from zero and counting from now, after which data should stop being

View File

@@ -82,6 +82,11 @@ public class NetworkStats implements Parcelable {
/** {@link #roaming} value where roaming data is accounted. */
public static final int ROAMING_YES = 1;
/** Denotes a request for stats at the interface level. */
public static final int STATS_PER_IFACE = 0;
/** Denotes a request for stats at the interface and UID level. */
public static final int STATS_PER_UID = 1;
// TODO: move fields to "mVariable" notation
/**

View File

@@ -281,7 +281,7 @@ interface INetworkManagementService
/**
* Return summary of network statistics all tethering interfaces.
*/
NetworkStats getNetworkStatsTethering();
NetworkStats getNetworkStatsTethering(int how);
/**
* Set quota for an interface.

View File

@@ -32,6 +32,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
@@ -1822,7 +1823,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats() {
public NetworkStats getTetherStats(int how) {
// We only need to return per-UID stats. Per-device stats are already counted by
// interface counters.
if (how != STATS_PER_UID) {
return new NetworkStats(SystemClock.elapsedRealtime(), 0);
}
final NativeDaemonEvent[] events;
try {
events = mConnector.executeForList("bandwidth", "gettetherstats");
@@ -1865,14 +1872,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
public NetworkStats getNetworkStatsTethering() {
public NetworkStats getNetworkStatsTethering(int how) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
synchronized (mTetheringStatsProviders) {
for (ITetheringStatsProvider provider: mTetheringStatsProviders.keySet()) {
try {
stats.combineAllValues(provider.getTetherStats());
stats.combineAllValues(provider.getTetherStats(how));
} catch (RemoteException e) {
Log.e(TAG, "Problem reading tethering stats from " +
mTetheringStatsProviders.get(provider) + ": " + e);

View File

@@ -17,7 +17,9 @@
package com.android.server.connectivity.tethering;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
@@ -202,16 +204,18 @@ public class OffloadController {
private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats() {
public NetworkStats getTetherStats(int how) {
NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
// We can't just post to mHandler because we are mostly (but not always) called by
// NetworkStatsService#performPollLocked, which is (currently) on the same thread as us.
mHandler.runWithScissors(() -> {
// We have to report both per-interface and per-UID stats, because offloaded traffic
// is not seen by kernel interface counters.
NetworkStats.Entry entry = new NetworkStats.Entry();
entry.set = SET_DEFAULT;
entry.tag = TAG_NONE;
entry.uid = UID_TETHERING;
entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
updateStatsForCurrentUpstream();

View File

@@ -31,6 +31,8 @@ import static android.net.NetworkStats.IFACE_ALL;
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.STATS_PER_IFACE;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
@@ -1032,6 +1034,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStats xtSnapshot = getNetworkStatsXt();
final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
// Tethering snapshot for dev and xt stats. Counts per-interface data from tethering stats
// providers that isn't already counted by dev and XT stats.
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE);
xtSnapshot.combineAllValues(tetherSnapshot);
devSnapshot.combineAllValues(tetherSnapshot);
// 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.
@@ -1328,14 +1335,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
// fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering();
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
uidSnapshot.combineAllValues(tetherSnapshot);
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
// fold video calling data usage stats into uid snapshot
final NetworkStats vtStats = telephonyManager.getVtDataUsage(true);
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
if (vtStats != null) {
uidSnapshot.combineAllValues(vtStats);
}
@@ -1354,7 +1361,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
Context.TELEPHONY_SERVICE);
// Merge video calling data usage into XT
final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(false);
final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(STATS_PER_IFACE);
if (vtSnapshot != null) {
xtSnapshot.combineAllValues(vtSnapshot);
}
@@ -1366,9 +1373,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* Return snapshot of current tethering statistics. Will return empty
* {@link NetworkStats} if any problems are encountered.
*/
private NetworkStats getNetworkStatsTethering() throws RemoteException {
private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
try {
return mNetworkManager.getNetworkStatsTethering();
return mNetworkManager.getNetworkStatsTethering(how);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
return new NetworkStats(0L, 10);

View File

@@ -6508,11 +6508,13 @@ public class TelephonyManager {
* Get aggregated video call data usage since boot.
* Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
*
* @param perUidStats True if requesting data usage per uid, otherwise overall usage.
* @param how one of the NetworkStats.STATS_PER_* constants depending on whether the request is
* for data usage per uid or overall usage.
* @return Snapshot of video call data usage
* @hide
*/
public NetworkStats getVtDataUsage(boolean perUidStats) {
public NetworkStats getVtDataUsage(int how) {
boolean perUidStats = (how == NetworkStats.STATS_PER_UID);
try {
ITelephony service = getITelephony();
if (service != null) {

View File

@@ -17,7 +17,10 @@
package com.android.server.connectivity.tethering;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_IFACE;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
@@ -377,7 +380,6 @@ public class OffloadControllerTest {
assertEquals(stats.txBytes, entry.txBytes);
assertEquals(SET_DEFAULT, entry.set);
assertEquals(TAG_NONE, entry.tag);
assertEquals(UID_TETHERING, entry.uid);
}
@Test
@@ -416,20 +418,33 @@ public class OffloadControllerTest {
ethernetStats.txBytes = 100000;
offload.setUpstreamLinkProperties(null);
NetworkStats stats = mTetherStatsProviderCaptor.getValue().getTetherStats();
ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
assertEquals(2, stats.size());
assertEquals(2, perUidStats.size());
NetworkStats.Entry entry = null;
for (int i = 0; i < stats.size(); i++) {
assertEquals(UID_ALL, stats.getValues(i, entry).uid);
assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid);
}
int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
int mobilePosition = 1 - ethernetPosition;
entry = stats.getValues(mobilePosition, entry);
assertNetworkStats(mobileIface, mobileStats, entry);
entry = perUidStats.getValues(mobilePosition, entry);
assertNetworkStats(mobileIface, mobileStats, entry);
ethernetStats.rxBytes = 12345 + 100000;
ethernetStats.txBytes = 54321 + 100000;
entry = stats.getValues(ethernetPosition, entry);
assertNetworkStats(ethernetIface, ethernetStats, entry);
entry = perUidStats.getValues(ethernetPosition, entry);
assertNetworkStats(ethernetIface, ethernetStats, entry);
}
@Test

View File

@@ -31,6 +31,8 @@ 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.STATS_PER_IFACE;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
@@ -823,17 +825,24 @@ public class NetworkStatsServiceTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectCurrentTime();
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
// Traffic seen by kernel counters (includes software tethering).
final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
// Hardware tethering traffic, not seen by kernel counters.
final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
// Traffic for UID_RED.
final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
final String[] tetherIfacePairs = new String[] { TEST_IFACE, "wlan0" };
// All tethering traffic, both hardware and software.
final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
.addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
0L);
expectNetworkStatsUidDetail(uidStats, tetherIfacePairs, tetherStats);
expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
expectNetworkStatsUidDetail(uidStats, tetherStats);
forcePollAndWaitForIdle();
// verify service recorded history
@@ -1013,10 +1022,16 @@ public class NetworkStatsServiceTest {
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
expectNetworkStatsSummary(summary, new NetworkStats(0L, 0));
}
private void expectNetworkStatsSummary(NetworkStats summary, NetworkStats tetherStats)
throws Exception {
when(mConnManager.getAllVpnInfo()).thenReturn(new VpnInfo[0]);
expectNetworkStatsSummaryDev(summary);
expectNetworkStatsSummaryXt(summary);
expectNetworkStatsTethering(STATS_PER_IFACE, tetherStats);
expectNetworkStatsSummaryDev(summary.clone());
expectNetworkStatsSummaryXt(summary.clone());
}
private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
@@ -1027,17 +1042,21 @@ public class NetworkStatsServiceTest {
when(mNetManager.getNetworkStatsSummaryXt()).thenReturn(summary);
}
private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
expectNetworkStatsUidDetail(detail, new String[0], new NetworkStats(0L, 0));
private void expectNetworkStatsTethering(int how, NetworkStats stats)
throws Exception {
when(mNetManager.getNetworkStatsTethering(how)).thenReturn(stats);
}
private void expectNetworkStatsUidDetail(
NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats)
private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0));
}
private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
throws Exception {
when(mNetManager.getNetworkStatsUidDetail(UID_ALL)).thenReturn(detail);
// also include tethering details, since they are folded into UID
when(mNetManager.getNetworkStatsTethering()).thenReturn(tetherStats);
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);
}
private void expectDefaultSettings() throws Exception {