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

Commit 370107cd authored by Chiachang Wang's avatar Chiachang Wang Committed by android-build-merger
Browse files

Follow up commit of aosp/1157146 am: 4336b91f am: 1b85572f

am: 921f4f22

Change-Id: I863c9620915ec39513860517fa5b2da5e13b6971
parents 5d852248 921f4f22
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -35,6 +35,14 @@ public class NetlinkConstants {
    private NetlinkConstants() {}

    public static final int NLA_ALIGNTO = 4;
    /**
     * Flag for dumping struct tcp_info.
     * Corresponding to enum definition in external/strace/linux/inet_diag.h.
     */
    public static final int INET_DIAG_MEMINFO = 1;

    public static final int SOCKDIAG_MSG_HEADER_SIZE =
            StructNlMsgHdr.STRUCT_SIZE + StructInetDiagMsg.STRUCT_SIZE;

    public static final int alignedLengthOf(short length) {
        final int intLength = (int) length & 0xffff;
+1 −1
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ public class DataStallUtils {
     * Type: int
     * Valid values: 0 to 100.
     */
    public static final String CONFIG_TCP_PACKETS_FAIL_RATE = "tcp_packets_fail_rate";
    public static final String CONFIG_TCP_PACKETS_FAIL_PERCENTAGE = "tcp_packets_fail_percentage";

    /** Corresponds to enum from bionic/libc/include/netinet/tcp.h. */
    public static final int TCP_ESTABLISHED = 1;
+100 −52
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@
package com.android.networkstack.netlink;

import static android.net.netlink.InetDiagMessage.InetDiagReqV2;
import static android.net.netlink.NetlinkConstants.INET_DIAG_MEMINFO;
import static android.net.netlink.NetlinkConstants.NLA_ALIGNTO;
import static android.net.netlink.NetlinkConstants.NLMSG_DONE;
import static android.net.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static android.net.util.DataStallUtils.CONFIG_MIN_PACKETS_THRESHOLD;
import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_RATE;
import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE;
import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD;
import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE;
import static android.net.util.DataStallUtils.TCP_MONITOR_STATE_FILTER;
@@ -35,13 +38,15 @@ import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOL_SOCKET;
import static android.system.OsConstants.SO_SNDTIMEO;

import android.content.Context;
import android.net.netlink.NetlinkSocket;
import android.net.netlink.StructInetDiagMsg;
import android.net.netlink.StructNlMsgHdr;
import android.net.util.NetworkStackUtils;
import android.net.util.SocketUtils;
import android.os.Build;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
@@ -53,7 +58,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.networkstack.apishim.ShimUtils;

import java.io.FileDescriptor;
import java.io.InterruptedIOException;
@@ -65,7 +69,7 @@ import java.util.List;
/**
 * Class for NetworkStack to send a SockDiag request and parse the returned tcp info.
 *
 * This should be only access from the NetworkMonitor statemahcine thread.
 * This is not thread-safe. This should be only accessed from one thread.
 */
public class TcpSocketTracker {
    private static final String TAG = "TcpSocketTracker";
@@ -75,16 +79,6 @@ public class TcpSocketTracker {
    private static final int DEFAULT_RECV_BUFSIZE = 60_000;
    // Default I/O timeout time in ms of the socket request.
    private static final long IO_TIMEOUT = 3_000L;
    // Map to definition in bionic/libc/kernel/uapi/linux/netlink.h.
    private static final int NLMSG_ALIGNTO = 4;
    /**
     * Flag for dumping struct tcp_info.
     * Corresponding to enum definition in external/strace/linux/inet_diag.h.
     */
    private static final int INET_DIAG_MEMINFO = 1;
    @VisibleForTesting
    public static final int SOCKDIAG_MSG_HEADER_SIZE =
            StructNlMsgHdr.STRUCT_SIZE + StructInetDiagMsg.STRUCT_SIZE;
    /** Cookie offset of an InetMagMessage header. */
    private static final int IDIAG_COOKIE_OFFSET = 44;
    /**
@@ -98,7 +92,9 @@ public class TcpSocketTracker {
    // Number of packets sent since the last received packet
    private int mSentSinceLastRecv;
    // The latest fail rate calculated by the latest tcp info.
    private int mLatestPacketFailRate;
    private int mLatestPacketFailPercentage;
    // Number of packets received in the latest polling cycle.
    private int mLatestReceivedCount;
    /**
     * Request to send to kernel to request tcp info.
     *
@@ -106,15 +102,30 @@ public class TcpSocketTracker {
     * Value: Bytes array represent the {@Code InetDiagReqV2}.
     */
    private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>();
    private final Dependencies mDependencies;
    private int mMinPacketsThreshold = DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD;
    private int mTcpPacketsFailRateThreshold = DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE;
    @VisibleForTesting
    public final Dependencies mDependencies;

    public TcpSocketTracker(Dependencies dps) {
    protected final DeviceConfig.OnPropertiesChangedListener mConfigListener =
            new DeviceConfig.OnPropertiesChangedListener() {
                @Override
                public void onPropertiesChanged(DeviceConfig.Properties properties) {
                    mMinPacketsThreshold = mDependencies.getDeviceConfigPropertyInt(
                            NAMESPACE_CONNECTIVITY,
                            CONFIG_MIN_PACKETS_THRESHOLD,
                            DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD);
                    mTcpPacketsFailRateThreshold = mDependencies.getDeviceConfigPropertyInt(
                            NAMESPACE_CONNECTIVITY,
                            CONFIG_TCP_PACKETS_FAIL_PERCENTAGE,
                            DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE);
                }
            };

    public TcpSocketTracker(@NonNull final Dependencies dps) {
        mDependencies = dps;
        // Request tcp info from NetworkStack directly needs extra SELinux permission added after Q
        // release.
        mDependencies = dps;
        if (!mDependencies.isTcpInfoParsingSupported()) return;

        // Build SocketDiag messages.
        for (final int family : ADDRESS_FAMILIES) {
            mSockDiagMsg.put(
@@ -128,14 +139,14 @@ public class TcpSocketTracker {
                            1 << INET_DIAG_MEMINFO /* idiagExt */,
                            TCP_MONITOR_STATE_FILTER));
        }
        mDependencies.addDeviceConfigChangedListener(mConfigListener);
    }

    /**
     * Request to send a SockDiag Netlink request. Receive and parse the returned message. This
     * function should only be called from statemachine thread of NetworkMonitor.
     * function is not thread-safe and should only be called from only one thread.
     *
     * @Return if this polling request executes successfully or not.
     *
     * TODO: Need to filter socket info based on the target network.
     */
    public boolean pollSocketsInfo() {
@@ -163,6 +174,10 @@ public class TcpSocketTracker {

                while (enoughBytesRemainForValidNlMsg(bytes)) {
                    final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes);
                    if (nlmsghdr == null) {
                        Log.e(TAG, "Badly formatted data.");
                        break;
                    }
                    final int nlmsgLen = nlmsghdr.nlmsg_len;
                    log("pollSocketsInfo: nlmsghdr=" + nlmsghdr);
                    if (nlmsghdr.nlmsg_type == NLMSG_DONE) break;
@@ -175,19 +190,20 @@ public class TcpSocketTracker {
                        // It's stored in native with 2 int. Parse it as long for convenience.
                        final long cookie = bytes.getLong();
                        // Skip the rest part of StructInetDiagMsg.
                        bytes.position(bytes.position() + 5 * Integer.BYTES);
                        final SocketInfo info =
                                parseSockInfo(bytes, family, nlmsgLen, time);
                        bytes.position(bytes.position()
                                + StructInetDiagMsg.STRUCT_SIZE - IDIAG_COOKIE_OFFSET - Long.BYTES);
                        final SocketInfo info = parseSockInfo(bytes, family, nlmsgLen, time);
                        // Update TcpStats based on previous and current socket info.
                        stat.accumulate(calculateLatestPacketsStat(info, mSocketInfos.get(cookie)));
                        mSocketInfos.put(cookie, info);
                    }
                }
            }
            // Calculate mSentSinceLastRecv and mLatestPacketFailRate.
            // Calculate mLatestReceiveCount, mSentSinceLastRecv and mLatestPacketFailPercentage.
            mSentSinceLastRecv = (stat.receivedCount == 0)
                    ? (mSentSinceLastRecv + stat.sentCount) : 0;
            mLatestPacketFailRate = ((stat.sentCount != 0)
            mLatestReceivedCount = stat.receivedCount;
            mLatestPacketFailPercentage = ((stat.sentCount != 0)
                    ? ((stat.retransmitCount + stat.lostCount) * 100 / stat.sentCount) : 0);

            // Remove out-of-date socket info.
@@ -252,20 +268,25 @@ public class TcpSocketTracker {
     */
    public boolean isDataStallSuspected() {
        if (!mDependencies.isTcpInfoParsingSupported()) return false;
        return (getLatestPacketFailRate() >= getTcpPacketsFailRateThreshold());
        return (getLatestPacketFailPercentage() >= getTcpPacketsFailRateThreshold());
    }

    /** Calculate the change between the {@param current} and {@param previous}. */
    @Nullable
    private TcpStat calculateLatestPacketsStat(@NonNull final SocketInfo current,
            @Nullable final SocketInfo previous) {
        final TcpStat stat = new TcpStat();

        if (current.tcpInfo != null) {
        if (current.tcpInfo == null) {
            log("Current tcpInfo is null.");
            return null;
        }

        stat.sentCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue();
        stat.receivedCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue();
        stat.lostCount = current.tcpInfo.getValue(TcpInfo.Field.LOST).intValue();
        stat.retransmitCount = current.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue();
        }

        if (previous != null && previous.tcpInfo != null) {
            stat.sentCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue();
            stat.receivedCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue();
@@ -278,12 +299,14 @@ public class TcpSocketTracker {

    /**
     * Get tcp connection fail rate based on packet lost and retransmission count.
     *
     * @return the latest packet fail percentage. -1 denotes that there is no available data.
     */
    public int getLatestPacketFailRate() {
        if (!mDependencies.isTcpInfoParsingSupported()) return 0;
    public int getLatestPacketFailPercentage() {
        if (!mDependencies.isTcpInfoParsingSupported()) return -1;
        // Only return fail rate if device sent enough packets.
        if (getSentSinceLastRecv() < getMinPacketsThreshold()) return 0;
        return mLatestPacketFailRate;
        if (getSentSinceLastRecv() < getMinPacketsThreshold()) return -1;
        return mLatestPacketFailPercentage;
    }

    /**
@@ -291,18 +314,14 @@ public class TcpSocketTracker {
     * between each polling period, not an accurate number.
     */
    public int getSentSinceLastRecv() {
        if (!mDependencies.isTcpInfoParsingSupported()) return 0;
        if (!mDependencies.isTcpInfoParsingSupported()) return -1;
        return mSentSinceLastRecv;
    }

    private int getMinPacketsThreshold() {
        return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
                CONFIG_MIN_PACKETS_THRESHOLD, DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD);
    }

    private int getTcpPacketsFailRateThreshold() {
        return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
                CONFIG_TCP_PACKETS_FAIL_RATE, DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE);
    /** Return the number of the packets received in the latest polling cycle. */
    public int getLatestReceivedCount() {
        if (!mDependencies.isTcpInfoParsingSupported()) return -1;
        return mLatestReceivedCount;
    }

    /** Check if the length and position of the given ByteBuffer is valid for a nlmsghdr message. */
@@ -315,6 +334,14 @@ public class TcpSocketTracker {
        return nlMsgLen >= SOCKDIAG_MSG_HEADER_SIZE;
    }

    private int getMinPacketsThreshold() {
        return mMinPacketsThreshold;
    }

    private int getTcpPacketsFailRateThreshold() {
        return mTcpPacketsFailRateThreshold;
    }

    /**
     * Method to skip the remaining attributes bytes.
     * Corresponds to NLMSG_NEXT in bionic/libc/kernel/uapi/linux/netlink.h.
@@ -324,12 +351,12 @@ public class TcpSocketTracker {
     */
    private void skipRemainingAttributesBytesAligned(@NonNull final ByteBuffer buffer,
            final int len) {
        // Data in {@Code RoutingAttribute} is followed after header with size {@Code NLMSG_ALIGNTO}
        // Data in {@Code RoutingAttribute} is followed after header with size {@Code NLA_ALIGNTO}
        // bytes long for each block. Next attribute will start after the padding bytes if any.
        // If all remaining bytes after header are valid in a data block, next attr will just start
        // after valid bytes.
        //
        // E.g. With NLMSG_ALIGNTO(4), an attr struct with length 5 means 1 byte valid data remains
        // E.g. With NLA_ALIGNTO(4), an attr struct with length 5 means 1 byte valid data remains
        // after header and 3(4-1) padding bytes. Next attr with length 8 will start after the
        // padding bytes and contain 4(8-4) valid bytes of data. The next attr start after the
        // valid bytes, like:
@@ -337,7 +364,7 @@ public class TcpSocketTracker {
        // [HEADER(L=5)][   4-Bytes DATA      ][ HEADER(L=8) ][4 bytes DATA][Next attr]
        // [ 5 valid bytes ][3 padding bytes  ][      8 valid bytes        ]   ...
        final int cur = buffer.position();
        buffer.position(cur + ((len + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)));
        buffer.position(cur + ((len + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)));
    }

    private void log(final String str) {
@@ -423,7 +450,9 @@ public class TcpSocketTracker {
        public int retransmitCount;
        public int receivedCount;

        void accumulate(final TcpStat stat) {
        void accumulate(@Nullable final TcpStat stat) {
            if (stat == null) return;

            sentCount += stat.sentCount;
            lostCount += stat.lostCount;
            receivedCount += stat.receivedCount;
@@ -431,12 +460,19 @@ public class TcpSocketTracker {
        }
    }


    /**
     * Dependencies class for testing.
     */
    @VisibleForTesting
    public static class Dependencies {
        private final Context mContext;
        private final boolean mIsTcpInfoParsingSupported;

        public Dependencies(final Context context, final boolean tcpSupport) {
            mContext = context;
            mIsTcpInfoParsingSupported = tcpSupport;
        }

        /**
         * Connect to kernel via netlink socket.
         *
@@ -451,6 +487,7 @@ public class TcpSocketTracker {

            return fd;
        }

        /**
         * Send composed message request to kernel.
         * @param fd see {@Code FileDescriptor}
@@ -484,7 +521,7 @@ public class TcpSocketTracker {
        public boolean isTcpInfoParsingSupported() {
            // Request tcp info from NetworkStack directly needs extra SELinux permission added
            // after Q release.
            return ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q);
            return mIsTcpInfoParsingSupported;
        }

        /**
@@ -494,5 +531,16 @@ public class TcpSocketTracker {
                throws ErrnoException, InterruptedIOException {
            return NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
        }

        public Context getContext() {
            return mContext;
        }

        /** Add device config change listener */
        public void addDeviceConfigChangedListener(
                @NonNull final DeviceConfig.OnPropertiesChangedListener listener) {
            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONNECTIVITY,
                    AsyncTask.THREAD_POOL_EXECUTOR, listener);
        }
    }
}
+9 −6
Original line number Diff line number Diff line
@@ -126,6 +126,7 @@ import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.TrafficStatsConstants;
import com.android.networkstack.R;
import com.android.networkstack.apishim.ShimUtils;
import com.android.networkstack.metrics.DataStallDetectionStats;
import com.android.networkstack.metrics.DataStallStatsUtils;
import com.android.networkstack.netlink.TcpSocketTracker;
@@ -389,13 +390,15 @@ public class NetworkMonitor extends StateMachine {
    public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
            SharedLog validationLog) {
        this(context, cb, network, new IpConnectivityLog(), validationLog,
                Dependencies.DEFAULT, new DataStallStatsUtils());
                Dependencies.DEFAULT, new DataStallStatsUtils(), new TcpSocketTracker(
                        new TcpSocketTracker.Dependencies(context,
                        ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q))));
    }

    @VisibleForTesting
    public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
            IpConnectivityLog logger, SharedLog validationLogs,
            Dependencies deps, DataStallStatsUtils detectionStatsUtils) {
            Dependencies deps, DataStallStatsUtils detectionStatsUtils, TcpSocketTracker tst) {
        // Add suffix indicating which NetworkMonitor we're talking about.
        super(TAG + "/" + network.toString());

@@ -442,7 +445,7 @@ public class NetworkMonitor extends StateMachine {
        mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
        mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
        mDataStallEvaluationType = getDataStallEvaluationType();
        mTcpTracker = new TcpSocketTracker(new TcpSocketTracker.Dependencies());
        mTcpTracker = tst;

        // Provide empty LinkProperties and NetworkCapabilities to make sure they are never null,
        // even before notifyNetworkConnected.
@@ -2139,7 +2142,7 @@ public class NetworkMonitor extends StateMachine {
        // 2. Accumulate enough packets count.
        // TODO: Need to filter per target network.
        if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP)) {
            if (getTcpSocketTracker().getSentSinceLastRecv() > 0) {
            if (getTcpSocketTracker().getLatestReceivedCount() > 0) {
                result = false;
            } else if (getTcpSocketTracker().isDataStallSuspected()) {
                result = true;
@@ -2160,8 +2163,8 @@ public class NetworkMonitor extends StateMachine {
        if (VDBG_STALL) {
            log("isDataStall: result=" + result + ", consecutive dns timeout count="
                    + mDnsStallDetector.getConsecutiveTimeoutCount()
                    + ", tcp packets received=" + getTcpSocketTracker().getSentSinceLastRecv()
                    + ", tcp fail rate=" + getTcpSocketTracker().getLatestPacketFailRate());
                    + ", tcp packets received=" + getTcpSocketTracker().getLatestReceivedCount()
                    + ", tcp fail rate=" + getTcpSocketTracker().getLatestPacketFailPercentage());
        }

        return (result == null) ? false : result;
+20 −23
Original line number Diff line number Diff line
@@ -16,13 +16,12 @@

package com.android.networkstack.netlink;

import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_RATE;
import static android.net.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE;
import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.system.OsConstants.AF_INET;

import static com.android.networkstack.netlink.TcpSocketTracker.SOCKDIAG_MSG_HEADER_SIZE;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -111,7 +110,7 @@ public class TcpSocketTrackerTest {
            "00000000" +        // data
            "0008" +            // len = 8
            "000F" +            // type = 15(INET_DIAG_MARK)
            "000C0064" +        // data, socket mark=786532
            "000C1A85" +        // data, socket mark=793221
            "00AC" +            // len = 172
            "0002" +            // type = 2(INET_DIAG_INFO)
            // tcp_info
@@ -129,7 +128,7 @@ public class TcpSocketTrackerTest {
            "00000218" +        // rcvMss = 536
            "00000000" +        // unsacked = 0
            "00000000" +        // acked = 0
            "00000005" +        // lost = 5
            "00000000" +        // lost = 0
            "00000000" +        // retrans = 0
            "00000000" +        // fackets = 0
            "000000BB" +        // lastDataSent = 187
@@ -185,7 +184,7 @@ public class TcpSocketTrackerTest {
        when(mDependencies.connectToKernel()).thenReturn(mMockFd);
        when(mDependencies.getDeviceConfigPropertyInt(
                eq(NAMESPACE_CONNECTIVITY),
                eq(CONFIG_TCP_PACKETS_FAIL_RATE),
                eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE),
                anyInt())).thenReturn(DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE);
    }

@@ -211,7 +210,7 @@ public class TcpSocketTrackerTest {
        expected.put(TcpInfo.Field.RCV_MSS, 536);
        expected.put(TcpInfo.Field.UNACKED, 0);
        expected.put(TcpInfo.Field.SACKED, 0);
        expected.put(TcpInfo.Field.LOST, 5);
        expected.put(TcpInfo.Field.LOST, 0);
        expected.put(TcpInfo.Field.RETRANS, 0);
        expected.put(TcpInfo.Field.FACKETS, 0);
        expected.put(TcpInfo.Field.LAST_DATA_SENT, 187);
@@ -242,7 +241,7 @@ public class TcpSocketTrackerTest {
        expected.put(TcpInfo.Field.DELIVERY_RATE, 0L);

        assertEquals(parsed.tcpInfo, new TcpInfo(expected));
        assertEquals(parsed.fwmark, 786532);
        assertEquals(parsed.fwmark, 793221);
        assertEquals(parsed.updateTime, 100);
        assertEquals(parsed.ipFamily, AF_INET);
    }
@@ -261,18 +260,6 @@ public class TcpSocketTrackerTest {
        assertFalse(TcpSocketTracker.enoughBytesRemainForValidNlMsg(buffer));
    }

    @Test
    public void testIsDataStallSuspected() {
        when(mDependencies.isTcpInfoParsingSupported()).thenReturn(false);
        final TcpSocketTracker tst = new TcpSocketTracker(mDependencies);
        assertFalse(tst.isDataStallSuspected());
        when(mDependencies.isTcpInfoParsingSupported()).thenReturn(true);
        assertFalse(tst.isDataStallSuspected());
        when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CONFIG_TCP_PACKETS_FAIL_RATE),
                anyInt())).thenReturn(0);
        assertTrue(tst.isDataStallSuspected());
    }

    @Test
    public void testPollSocketsInfo() throws Exception {
        when(mDependencies.isTcpInfoParsingSupported()).thenReturn(false);
@@ -284,20 +271,30 @@ public class TcpSocketTrackerTest {
        final ByteBuffer invalidBuffer = ByteBuffer.allocate(1);
        when(mDependencies.recvMesssage(any())).thenReturn(invalidBuffer);
        assertTrue(tst.pollSocketsInfo());
        assertEquals(0, tst.getLatestPacketFailRate());
        assertEquals(-1, tst.getLatestPacketFailPercentage());
        assertEquals(0, tst.getSentSinceLastRecv());

        // Header only.
        final ByteBuffer headerBuffer = ByteBuffer.wrap(SOCK_DIAG_MSG_BYTES);
        when(mDependencies.recvMesssage(any())).thenReturn(headerBuffer);
        assertTrue(tst.pollSocketsInfo());
        assertEquals(-1, tst.getLatestPacketFailPercentage());
        assertEquals(0, tst.getSentSinceLastRecv());
        assertEquals(0, tst.getLatestPacketFailRate());

        final ByteBuffer tcpBuffer = ByteBuffer.wrap(TEST_RESPONSE_BYTES);
        when(mDependencies.recvMesssage(any())).thenReturn(tcpBuffer);
        assertTrue(tst.pollSocketsInfo());

        assertEquals(10, tst.getSentSinceLastRecv());
        assertEquals(100, tst.getLatestPacketFailRate());
        assertEquals(50, tst.getLatestPacketFailPercentage());
        assertFalse(tst.isDataStallSuspected());
        // Lower the threshold.
        when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE),
                anyInt())).thenReturn(40);
        // No device config change. Using cache value.
        assertFalse(tst.isDataStallSuspected());
        // Trigger a config update
        tst.mConfigListener.onPropertiesChanged(null /* properties */);
        assertTrue(tst.isDataStallSuspected());
    }
}
Loading