Merge "Record even more stats even more often"

This commit is contained in:
Treehugger Robot
2017-09-04 02:11:47 +00:00
committed by Gerrit Code Review
2 changed files with 121 additions and 19 deletions

View File

@@ -63,6 +63,8 @@ import java.util.concurrent.TimeUnit;
*/
public class OffloadController {
private static final String TAG = OffloadController.class.getSimpleName();
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
@@ -148,6 +150,14 @@ public class OffloadController {
public void onStoppedUnsupported() {
if (!started()) return;
mLog.log("onStoppedUnsupported");
// Poll for statistics and trigger a sweep of tethering
// stats by observers. This might not succeed, but it's
// worth trying anyway. We need to do this because from
// this point on we continue with software forwarding,
// and we need to synchronize stats and limits between
// software and hardware forwarding.
updateStatsForAllUpstreams();
forceTetherStatsPoll();
}
@Override
@@ -155,11 +165,15 @@ public class OffloadController {
if (!started()) return;
mLog.log("onSupportAvailable");
// [1] Poll for statistics and notify NetworkStats
// [2] (Re)Push all state:
// [a] push local prefixes
// [b] push downstreams
// [c] push upstream parameters
// [1] Poll for statistics and trigger a sweep of stats
// by observers. We need to do this to ensure that any
// limits set take into account any software tethering
// traffic that has been happening in the meantime.
updateStatsForAllUpstreams();
forceTetherStatsPoll();
// [2] (Re)Push all state.
// TODO: computeAndPushLocalPrefixes()
// TODO: push all downstream state.
pushUpstreamParameters(null);
}
@@ -181,12 +195,7 @@ public class OffloadController {
// 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);
}
forceTetherStatsPoll();
}
@Override
@@ -305,13 +314,33 @@ public class OffloadController {
maybeUpdateStats(currentUpstreamInterface());
}
private void updateStatsForAllUpstreams() {
// In practice, there should only ever be a single digit number of
// upstream interfaces over the lifetime of an active tethering session.
// Roughly speaking, imagine a very ambitious one or two of each of the
// following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ].
for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
maybeUpdateStats(kv.getKey());
}
}
private void forceTetherStatsPoll() {
try {
mNms.tetherLimitReached(mStatsProvider);
} catch (RemoteException e) {
mLog.e("Cannot report data limit reached: " + e);
}
}
public void setUpstreamLinkProperties(LinkProperties lp) {
if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
String prevUpstream = (mUpstreamLinkProperties != null) ?
mUpstreamLinkProperties.getInterfaceName() : null;
final String prevUpstream = currentUpstreamInterface();
mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
// Make sure we record this interface in the ForwardedStats map.
final String iface = currentUpstreamInterface();
if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS);
// TODO: examine return code and decide what to do if programming
// upstream parameters fails (probably just wait for a subsequent
@@ -378,16 +407,20 @@ public class OffloadController {
}
private boolean pushUpstreamParameters(String prevUpstream) {
if (mUpstreamLinkProperties == null) {
final String iface = currentUpstreamInterface();
if (TextUtils.isEmpty(iface)) {
final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null);
// Update stats after we've told the hardware to stop forwarding so
// we don't miss packets.
maybeUpdateStats(prevUpstream);
return mHwInterface.setUpstreamParameters(null, null, null, null);
return rval;
}
// A stacked interface cannot be an upstream for hardware offload.
// Consequently, we examine only the primary interface name, look at
// getAddresses() rather than getAllAddresses(), and check getRoutes()
// rather than getAllRoutes().
final String iface = mUpstreamLinkProperties.getInterfaceName();
final ArrayList<String> v6gateways = new ArrayList<>();
String v4addr = null;
String v4gateway = null;

View File

@@ -32,12 +32,13 @@ import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -441,6 +442,9 @@ public class OffloadControllerTest {
ethernetStats.txBytes = 100000;
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
offload.setUpstreamLinkProperties(null);
// Expect that we first clear the HAL's upstream parameters.
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null));
// Expect that we fetch stats from the previous upstream.
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
@@ -450,8 +454,6 @@ public class OffloadControllerTest {
waitForIdle();
// There is no current upstream, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(null), eq(null), eq(null), eq(null));
inOrder.verifyNoMoreInteractions();
assertEquals(2, stats.size());
@@ -626,6 +628,73 @@ public class OffloadControllerTest {
inOrder.verifyNoMoreInteractions();
}
@Test
public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception {
setupFunctioningHardwareInterface();
enableOffload();
final OffloadController offload = makeOffloadController();
offload.start();
// Pretend to set a few different upstreams (only the interface name
// matters for this test; we're ignoring IP and route information).
final LinkProperties upstreamLp = new LinkProperties();
for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) {
upstreamLp.setInterfaceName(ifname);
offload.setUpstreamLinkProperties(upstreamLp);
}
// Clear invocation history, especially the getForwardedStats() calls
// that happen with setUpstreamParameters().
clearInvocations(mHardware);
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedUnsupported();
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
verifyNoMoreInteractions(mHardware);
verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
verifyNoMoreInteractions(mNMService);
}
@Test
public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters()
throws Exception {
setupFunctioningHardwareInterface();
enableOffload();
final OffloadController offload = makeOffloadController();
offload.start();
// Pretend to set a few different upstreams (only the interface name
// matters for this test; we're ignoring IP and route information).
final LinkProperties upstreamLp = new LinkProperties();
for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) {
upstreamLp.setInterfaceName(ifname);
offload.setUpstreamLinkProperties(upstreamLp);
}
// Clear invocation history, especially the getForwardedStats() calls
// that happen with setUpstreamParameters().
clearInvocations(mHardware);
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onSupportAvailable();
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
verifyNoMoreInteractions(mNMService);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any());
verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong());
verifyNoMoreInteractions(mHardware);
}
private static void assertArrayListContains(ArrayList<String> list, String... elems) {
for (String element : elems) {
assertTrue(list.contains(element));