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

Commit bbadcfd3 authored by Hugo Benichi's avatar Hugo Benichi Committed by android-build-merger
Browse files

Merge "Tethering offload stats updates are eventually consistent"

am: ff12ba93

Change-Id: I3a8ddc928b60c666faab2991defe8fb7afd0801e
parents aeb64f7f ff12ba93
Loading
Loading
Loading
Loading
+42 −25
Original line number Original line Diff line number Diff line
@@ -32,11 +32,13 @@ import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.RouteInfo;
import android.net.util.SharedLog;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.INetworkManagementService;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;


import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IndentingPrintWriter;


@@ -46,8 +48,10 @@ import java.net.InetAddress;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Objects;
import java.util.Set;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;


/**
/**
@@ -79,8 +83,8 @@ public class OffloadController {
    // Maps upstream interface names to offloaded traffic statistics.
    // Maps upstream interface names to offloaded traffic statistics.
    // Always contains the latest value received from the hardware for each interface, regardless of
    // Always contains the latest value received from the hardware for each interface, regardless of
    // whether offload is currently running on that interface.
    // whether offload is currently running on that interface.
    private HashMap<String, OffloadHardwareInterface.ForwardedStats>
    private ConcurrentHashMap<String, ForwardedStats> mForwardedStats =
            mForwardedStats = new HashMap<>();
            new ConcurrentHashMap<>(16, 0.75F, 1);


    // Maps upstream interface names to interface quotas.
    // Maps upstream interface names to interface quotas.
    // Always contains the latest value received from the framework for each interface, regardless
    // Always contains the latest value received from the framework for each interface, regardless
@@ -205,27 +209,29 @@ public class OffloadController {
    private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
    private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
        @Override
        @Override
        public NetworkStats getTetherStats(int how) {
        public NetworkStats getTetherStats(int how) {
            NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
            // getTetherStats() is the only function in OffloadController that can be called from
            // a different thread. Do not attempt to update stats by querying the offload HAL
            // synchronously from a different thread than our Handler thread. http://b/64771555.
            Runnable updateStats = () -> { updateStatsForCurrentUpstream(); };
            if (Looper.myLooper() == mHandler.getLooper()) {
                updateStats.run();
            } else {
                mHandler.post(updateStats);
            }


            // We can't just post to mHandler because we are mostly (but not always) called by
            NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
            // 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();
            NetworkStats.Entry entry = new NetworkStats.Entry();
            entry.set = SET_DEFAULT;
            entry.set = SET_DEFAULT;
            entry.tag = TAG_NONE;
            entry.tag = TAG_NONE;
            entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
            entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;


                updateStatsForCurrentUpstream();
            for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {

                ForwardedStats value = kv.getValue();
                for (String iface : mForwardedStats.keySet()) {
                entry.iface = kv.getKey();
                    entry.iface = iface;
                entry.rxBytes = value.rxBytes;
                    entry.rxBytes = mForwardedStats.get(iface).rxBytes;
                entry.txBytes = value.txBytes;
                    entry.txBytes = mForwardedStats.get(iface).txBytes;
                stats.addValues(entry);
                stats.addValues(entry);
            }
            }
            }, 0);


            return stats;
            return stats;
        }
        }
@@ -247,10 +253,21 @@ public class OffloadController {
            return;
            return;
        }
        }


        if (!mForwardedStats.containsKey(iface)) {
        // Always called on the handler thread.
            mForwardedStats.put(iface, new OffloadHardwareInterface.ForwardedStats());
        //
        }
        // Use get()/put() instead of updating ForwardedStats in place because we can be called
        mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
        // concurrently with getTetherStats. In combination with the guarantees provided by
        // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of
        // the stats for each interface, and does not observe partial writes where rxBytes is
        // updated and txBytes is not.
        ForwardedStats diff = mHwInterface.getForwardedStats(iface);
        ForwardedStats base = mForwardedStats.get(iface);
        if (base != null) {
            diff.add(base);
        }
        mForwardedStats.put(iface, diff);
        // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from
        // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately.
    }
    }


    private boolean maybeUpdateDataLimit(String iface) {
    private boolean maybeUpdateDataLimit(String iface) {
+16 −0
Original line number Original line Diff line number Diff line
@@ -400,23 +400,39 @@ public class OffloadControllerTest {
        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);


        InOrder inOrder = inOrder(mHardware);

        final LinkProperties lp = new LinkProperties();
        final LinkProperties lp = new LinkProperties();
        lp.setInterfaceName(ethernetIface);
        lp.setInterfaceName(ethernetIface);
        offload.setUpstreamLinkProperties(lp);
        offload.setUpstreamLinkProperties(lp);
        // Previous upstream was null, so no stats are fetched.
        inOrder.verify(mHardware, never()).getForwardedStats(any());


        lp.setInterfaceName(mobileIface);
        lp.setInterfaceName(mobileIface);
        offload.setUpstreamLinkProperties(lp);
        offload.setUpstreamLinkProperties(lp);
        // Expect that we fetch stats from the previous upstream.
        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));


        lp.setInterfaceName(ethernetIface);
        lp.setInterfaceName(ethernetIface);
        offload.setUpstreamLinkProperties(lp);
        offload.setUpstreamLinkProperties(lp);
        // Expect that we fetch stats from the previous upstream.
        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));


        ethernetStats = new ForwardedStats();
        ethernetStats.rxBytes = 100000;
        ethernetStats.rxBytes = 100000;
        ethernetStats.txBytes = 100000;
        ethernetStats.txBytes = 100000;
        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
        offload.setUpstreamLinkProperties(null);
        offload.setUpstreamLinkProperties(null);
        // Expect that we fetch stats from the previous upstream.
        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));


        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
        NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
        NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
        NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
        NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
        waitForIdle();
        // There is no current upstream, so no stats are fetched.
        inOrder.verify(mHardware, never()).getForwardedStats(eq(ethernetIface));
        inOrder.verifyNoMoreInteractions();


        assertEquals(2, stats.size());
        assertEquals(2, stats.size());
        assertEquals(2, perUidStats.size());
        assertEquals(2, perUidStats.size());