Merge changes I325b13d5,I89719fe7

* changes:
  Add tether offload traffic to interface stats as well.
  Tell the system when tethering offload hits a limit.
This commit is contained in:
Treehugger Robot
2017-08-19 03:05:14 +00:00
committed by Gerrit Code Review
9 changed files with 157 additions and 31 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

@@ -219,6 +219,21 @@ interface INetworkManagementService
*/
void unregisterTetheringStatsProvider(ITetheringStatsProvider provider);
/**
* Reports that a tethering provider has reached a data limit.
*
* Currently triggers a global alert, which causes NetworkStatsService to poll counters and
* re-evaluate data usage.
*
* This does not take an interface name because:
* 1. The tethering offload stats provider cannot reliably determine the interface on which the
* limit was reached, because the HAL does not provide it.
* 2. Firing an interface-specific alert instead of a global alert isn't really useful since in
* all cases of interest, the system responds to both in the same way - it polls stats, and
* then notifies NetworkPolicyManagerService of the fact.
*/
void tetherLimitReached(ITetheringStatsProvider provider);
/**
** PPPD
**/
@@ -266,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;
@@ -529,6 +530,18 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
@Override
public void tetherLimitReached(ITetheringStatsProvider provider) {
mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
synchronized(mTetheringStatsProviders) {
if (!mTetheringStatsProviders.containsKey(provider)) {
return;
}
// No current code examines the interface parameter in a global alert. Just pass null.
notifyLimitReached(LIMIT_GLOBAL_ALERT, null);
}
}
// Sync the state of the given chain with the native daemon.
private void syncFirewallChainLocked(int chain, SparseIntArray uidFirewallRules, String name) {
int size = uidFirewallRules.size();
@@ -1810,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");
@@ -1853,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;
@@ -60,6 +62,8 @@ public class OffloadController {
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
private final INetworkManagementService mNms;
private final ITetheringStatsProvider mStatsProvider;
private final SharedLog mLog;
private boolean mConfigInitialized;
private boolean mControlInitialized;
@@ -89,13 +93,14 @@ public class OffloadController {
mHandler = h;
mHwInterface = hwi;
mContentResolver = contentResolver;
mNms = nms;
mStatsProvider = new OffloadTetheringStatsProvider();
mLog = log.forSubComponent(TAG);
mExemptPrefixes = new HashSet<>();
mLastLocalPrefixStrs = new HashSet<>();
try {
nms.registerTetheringStatsProvider(
new OffloadTetheringStatsProvider(), getClass().getSimpleName());
mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName());
} catch (RemoteException e) {
mLog.e("Cannot register offload stats provider: " + e);
}
@@ -150,7 +155,26 @@ public class OffloadController {
@Override
public void onStoppedLimitReached() {
mLog.log("onStoppedLimitReached");
// Poll for statistics and notify NetworkStats
// We cannot reliably determine on which interface the limit was reached,
// because the HAL interface does not specify it. We cannot just use the
// current upstream, because that might have changed since the time that
// the HAL queued the callback.
// TODO: rev the HAL so that it provides an interface name.
// Fetch current stats, so that when our notification reaches
// NetworkStatsService and triggers a poll, we will respond with
// current data (which will be above the limit that was reached).
// Note that if we just changed upstream, this is unnecessary but harmless.
// The stats for the previous upstream were already updated on this thread
// just after the upstream was changed, so they are also up-to-date.
updateStatsForCurrentUpstream();
try {
mNms.tetherLimitReached(mStatsProvider);
} catch (RemoteException e) {
mLog.e("Cannot report data limit reached: " + e);
}
}
@Override
@@ -180,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;
@@ -85,6 +88,8 @@ public class OffloadControllerTest {
ArgumentCaptor.forClass(ArrayList.class);
private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
private MockContentResolver mContentResolver;
@Before public void setUp() {
@@ -105,7 +110,7 @@ public class OffloadControllerTest {
private void setupFunctioningHardwareInterface() {
when(mHardware.initOffloadConfig()).thenReturn(true);
when(mHardware.initOffloadControl(any(OffloadHardwareInterface.ControlCallback.class)))
when(mHardware.initOffloadControl(mControlCallbackCaptor.capture()))
.thenReturn(true);
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
}
@@ -375,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
@@ -414,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
@@ -493,4 +510,17 @@ public class OffloadControllerTest {
waitForIdle();
inOrder.verify(mHardware).stopOffloadControl();
}
@Test
public void testDataLimitCallback() throws Exception {
setupFunctioningHardwareInterface();
enableOffload();
final OffloadController offload = makeOffloadController();
offload.start();
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
}
}

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 {