Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aabdaa97 authored by Erik Kline's avatar Erik Kline
Browse files

Record even more stats even more often

Like kale, one can never have enough stats.  =)

Test: as follows
    - built
    - flashed
    - booted
    - runtest frameworks-net passes
Bug: 29337859
Bug: 32163131
Merged-In: I5d40eae488cab685be6a44849181c0286fe28fdb
Merged-In: I759e97f9a72d15a84036c3a56451b872143539c6
Change-Id: Ieb47c3beed50f21c2c858fe57438afd48cfdc662
(cherry picked from commit 1199a352)
parent 63915984
Loading
Loading
Loading
Loading
+49 −16
Original line number Original line Diff line number Diff line
@@ -63,6 +63,8 @@ import java.util.concurrent.TimeUnit;
 */
 */
public class OffloadController {
public class OffloadController {
    private static final String TAG = OffloadController.class.getSimpleName();
    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 Handler mHandler;
    private final OffloadHardwareInterface mHwInterface;
    private final OffloadHardwareInterface mHwInterface;
@@ -148,6 +150,14 @@ public class OffloadController {
                    public void onStoppedUnsupported() {
                    public void onStoppedUnsupported() {
                        if (!started()) return;
                        if (!started()) return;
                        mLog.log("onStoppedUnsupported");
                        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
                    @Override
@@ -155,11 +165,15 @@ public class OffloadController {
                        if (!started()) return;
                        if (!started()) return;
                        mLog.log("onSupportAvailable");
                        mLog.log("onSupportAvailable");


                        // [1] Poll for statistics and notify NetworkStats
                        // [1] Poll for statistics and trigger a sweep of stats
                        // [2] (Re)Push all state:
                        // by observers. We need to do this to ensure that any
                        //     [a] push local prefixes
                        // limits set take into account any software tethering
                        //     [b] push downstreams
                        // traffic that has been happening in the meantime.
                        //     [c] push upstream parameters
                        updateStatsForAllUpstreams();
                        forceTetherStatsPoll();
                        // [2] (Re)Push all state.
                        // TODO: computeAndPushLocalPrefixes()
                        // TODO: push all downstream state.
                        pushUpstreamParameters(null);
                        pushUpstreamParameters(null);
                    }
                    }


@@ -181,12 +195,7 @@ public class OffloadController {
                        // The stats for the previous upstream were already updated on this thread
                        // 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.
                        // just after the upstream was changed, so they are also up-to-date.
                        updateStatsForCurrentUpstream();
                        updateStatsForCurrentUpstream();

                        forceTetherStatsPoll();
                        try {
                            mNms.tetherLimitReached(mStatsProvider);
                        } catch (RemoteException e) {
                            mLog.e("Cannot report data limit reached: " + e);
                        }
                    }
                    }


                    @Override
                    @Override
@@ -305,13 +314,33 @@ public class OffloadController {
        maybeUpdateStats(currentUpstreamInterface());
        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) {
    public void setUpstreamLinkProperties(LinkProperties lp) {
        if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
        if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;


        String prevUpstream = (mUpstreamLinkProperties != null) ?
        final String prevUpstream = currentUpstreamInterface();
                mUpstreamLinkProperties.getInterfaceName() : null;


        mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
        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
        // TODO: examine return code and decide what to do if programming
        // upstream parameters fails (probably just wait for a subsequent
        // upstream parameters fails (probably just wait for a subsequent
@@ -378,16 +407,20 @@ public class OffloadController {
    }
    }


    private boolean pushUpstreamParameters(String prevUpstream) {
    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);
            maybeUpdateStats(prevUpstream);
            return mHwInterface.setUpstreamParameters(null, null, null, null);
            return rval;
        }
        }


        // A stacked interface cannot be an upstream for hardware offload.
        // A stacked interface cannot be an upstream for hardware offload.
        // Consequently, we examine only the primary interface name, look at
        // Consequently, we examine only the primary interface name, look at
        // getAddresses() rather than getAllAddresses(), and check getRoutes()
        // getAddresses() rather than getAllAddresses(), and check getRoutes()
        // rather than getAllRoutes().
        // rather than getAllRoutes().
        final String iface = mUpstreamLinkProperties.getInterfaceName();
        final ArrayList<String> v6gateways = new ArrayList<>();
        final ArrayList<String> v6gateways = new ArrayList<>();
        String v4addr = null;
        String v4addr = null;
        String v4gateway = null;
        String v4gateway = null;
+72 −3
Original line number Original line Diff line number Diff line
@@ -32,12 +32,13 @@ import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
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.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.content.Context;
import android.content.Context;
@@ -441,6 +442,9 @@ public class OffloadControllerTest {
        ethernetStats.txBytes = 100000;
        ethernetStats.txBytes = 100000;
        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
        offload.setUpstreamLinkProperties(null);
        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.
        // Expect that we fetch stats from the previous upstream.
        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));


@@ -450,8 +454,6 @@ public class OffloadControllerTest {
        waitForIdle();
        waitForIdle();
        // There is no current upstream, so no stats are fetched.
        // There is no current upstream, so no stats are fetched.
        inOrder.verify(mHardware, never()).getForwardedStats(any());
        inOrder.verify(mHardware, never()).getForwardedStats(any());
        inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                eq(null), eq(null), eq(null), eq(null));
        inOrder.verifyNoMoreInteractions();
        inOrder.verifyNoMoreInteractions();


        assertEquals(2, stats.size());
        assertEquals(2, stats.size());
@@ -626,6 +628,73 @@ public class OffloadControllerTest {
        inOrder.verifyNoMoreInteractions();
        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) {
    private static void assertArrayListContains(ArrayList<String> list, String... elems) {
        for (String element : elems) {
        for (String element : elems) {
            assertTrue(list.contains(element));
            assertTrue(list.contains(element));