summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChiachang Wang <chiachangwang@google.com>2020-05-21 07:03:23 +0000
committerChiachang Wang <chiachangwang@google.com>2020-05-21 10:10:25 +0000
commit0b34ae627c27877412d800dfc0e1883f8ca0b5e3 (patch)
tree40c8f9835234acee19b10a2e995f07004a804f76
parent5abdc91e97ae900b3a6840a1089244768a09464f (diff)
add TCP data stall metrics
Current metrics contains only DNS signal. Given TCP info is also considered to detection data stall, the related TCP information should be included into metrics. Bug: 150182763 Test: atest NetworkStackTests NetworkStackNextTests Test: Test with ./out/host/linux-x86/bin/statsd_testdrive 121 to ensure log properly Merged-In: I5e306299f3cf4f49c46f161cd0aa4cb40d1ab76e Change-Id: I5e306299f3cf4f49c46f161cd0aa4cb40d1ab76e
-rw-r--r--src/android/net/util/DataStallUtils.java15
-rw-r--r--src/com/android/networkstack/metrics/DataStallDetectionStats.java102
-rw-r--r--src/com/android/networkstack/metrics/DataStallStatsUtils.java4
-rwxr-xr-xsrc/com/android/server/connectivity/NetworkMonitor.java48
-rw-r--r--tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java367
5 files changed, 438 insertions, 98 deletions
diff --git a/src/android/net/util/DataStallUtils.java b/src/android/net/util/DataStallUtils.java
index 5787879..3391a71 100644
--- a/src/android/net/util/DataStallUtils.java
+++ b/src/android/net/util/DataStallUtils.java
@@ -16,15 +16,30 @@
package android.net.util;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Collection of utilities for data stall.
*/
public class DataStallUtils {
+ public static final int DATA_STALL_EVALUATION_TYPE_NONE = 0;
/** Detect data stall using dns timeout counts. */
public static final int DATA_STALL_EVALUATION_TYPE_DNS = 1 << 0;
/** Detect data stall using tcp connection fail rate. */
public static final int DATA_STALL_EVALUATION_TYPE_TCP = 1 << 1;
+ @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" }, value = {
+ DATA_STALL_EVALUATION_TYPE_NONE,
+ DATA_STALL_EVALUATION_TYPE_DNS,
+ DATA_STALL_EVALUATION_TYPE_TCP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EvaluationType {
+ }
+
// Default configuration values for data stall detection.
public static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
public static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
diff --git a/src/com/android/networkstack/metrics/DataStallDetectionStats.java b/src/com/android/networkstack/metrics/DataStallDetectionStats.java
index 7a1f9ac..d294e04 100644
--- a/src/com/android/networkstack/metrics/DataStallDetectionStats.java
+++ b/src/com/android/networkstack/metrics/DataStallDetectionStats.java
@@ -19,8 +19,10 @@ package com.android.networkstack.metrics;
import android.net.util.NetworkStackUtils;
import android.net.wifi.WifiInfo;
+import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.server.connectivity.nano.CellularData;
@@ -41,18 +43,35 @@ import java.util.Objects;
* @hide
*/
public final class DataStallDetectionStats {
- private static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+ public static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+ // Default value of TCP signals.
+ @VisibleForTesting
+ public static final int UNSPECIFIED_TCP_FAIL_RATE = -1;
+ @VisibleForTesting
+ public static final int UNSPECIFIED_TCP_PACKETS_COUNT = -1;
+ @VisibleForTesting
@NonNull
- final byte[] mCellularInfo;
+ public final byte[] mCellularInfo;
+ @VisibleForTesting
@NonNull
- final byte[] mWifiInfo;
+ public final byte[] mWifiInfo;
@NonNull
- final byte[] mDns;
- final int mEvaluationType;
- final int mNetworkType;
+ public final byte[] mDns;
+ @VisibleForTesting
+ public final int mEvaluationType;
+ @VisibleForTesting
+ public final int mNetworkType;
+ // The TCP packets fail rate percentage from the latest tcp polling. -1 means the TCP signal is
+ // not known or not supported on the SDK version of this device.
+ @VisibleForTesting @IntRange(from = -1, to = 100)
+ public final int mTcpFailRate;
+ // Number of packets sent since the last received packet.
+ @VisibleForTesting
+ public final int mTcpSentSinceLastRecv;
public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi,
- @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) {
+ @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType,
+ int failRate, int sentSinceLastRecv) {
mCellularInfo = emptyCellDataIfNull(cell);
mWifiInfo = emptyWifiInfoIfNull(wifi);
@@ -62,9 +81,20 @@ public final class DataStallDetectionStats {
mDns = MessageNano.toByteArray(dns);
mEvaluationType = evalType;
mNetworkType = netType;
+ mTcpFailRate = failRate;
+ mTcpSentSinceLastRecv = sentSinceLastRecv;
}
- private byte[] emptyCellDataIfNull(@Nullable byte[] cell) {
+ /**
+ * Because metrics data must contain data for each field even if it's not supported or not
+ * available, generate a byte array representing an empty {@link CellularData} if the
+ * {@link CellularData} is unavailable.
+ *
+ * @param cell a byte array representing current {@link CellularData} of {@code this}
+ * @return a byte array of a {@link CellularData}.
+ */
+ @VisibleForTesting
+ public static byte[] emptyCellDataIfNull(@Nullable byte[] cell) {
if (cell != null) return cell;
CellularData data = new CellularData();
@@ -75,7 +105,16 @@ public final class DataStallDetectionStats {
return MessageNano.toByteArray(data);
}
- private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) {
+ /**
+ * Because metrics data must contain data for each field even if it's not supported or not
+ * available, generate a byte array representing an empty {@link WifiData} if the
+ * {@link WiFiData} is unavailable.
+ *
+ * @param wifi a byte array representing current {@link WiFiData} of {@code this}.
+ * @return a byte array of a {@link WiFiData}.
+ */
+ @VisibleForTesting
+ public static byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) {
if (wifi != null) return wifi;
WifiData data = new WifiData();
@@ -95,7 +134,11 @@ public final class DataStallDetectionStats {
.append(", cell info: ")
.append(HexDump.toHexString(mCellularInfo))
.append(", dns: ")
- .append(HexDump.toHexString(mDns));
+ .append(HexDump.toHexString(mDns))
+ .append(", tcp fail rate: ")
+ .append(mTcpFailRate)
+ .append(", tcp received: ")
+ .append(mTcpSentSinceLastRecv);
return sb.toString();
}
@@ -107,12 +150,15 @@ public final class DataStallDetectionStats {
&& (mEvaluationType == other.mEvaluationType)
&& Arrays.equals(mWifiInfo, other.mWifiInfo)
&& Arrays.equals(mCellularInfo, other.mCellularInfo)
- && Arrays.equals(mDns, other.mDns);
+ && Arrays.equals(mDns, other.mDns)
+ && (mTcpFailRate == other.mTcpFailRate)
+ && (mTcpSentSinceLastRecv == other.mTcpSentSinceLastRecv);
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns);
+ return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns,
+ mTcpFailRate, mTcpSentSinceLastRecv);
}
/**
@@ -131,6 +177,8 @@ public final class DataStallDetectionStats {
private final List<Long> mDnsTimeStamp = new ArrayList<Long>();
private int mEvaluationType;
private int mNetworkType;
+ private int mTcpFailRate = UNSPECIFIED_TCP_FAIL_RATE;
+ private int mTcpSentSinceLastRecv = UNSPECIFIED_TCP_PACKETS_COUNT;
/**
* Add a dns event into Builder.
@@ -168,6 +216,34 @@ public final class DataStallDetectionStats {
}
/**
+ * Set the TCP packet fail rate into Builder. The data is included since android R.
+ *
+ * @param rate the TCP packet fail rate of the logged network. The default value is
+ * {@code UNSPECIFIED_TCP_FAIL_RATE}, which means the TCP signal is not known
+ * or not supported on the SDK version of this device.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setTcpFailRate(@IntRange(from = -1, to = 100) int rate) {
+ mTcpFailRate = rate;
+ return this;
+ }
+
+ /**
+ * Set the number of TCP packets sent since the last received packet into Builder. The data
+ * starts to be included since android R.
+ *
+ * @param count the number of packets sent since the last received packet of the logged
+ * network. Keep it unset as default value or set to
+ * {@code UNSPECIFIED_TCP_PACKETS_COUNT} if the tcp signal is unsupported with
+ * current device android sdk version or the packets count is unknown.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setTcpSentSinceLastRecv(int count) {
+ mTcpSentSinceLastRecv = count;
+ return this;
+ }
+
+ /**
* Set the wifi data into Builder.
*
* @param info a {@link WifiInfo} of the connected wifi network.
@@ -223,7 +299,7 @@ public final class DataStallDetectionStats {
return new DataStallDetectionStats(mCellularInfo, mWifiInfo,
NetworkStackUtils.convertToIntArray(mDnsReturnCode),
NetworkStackUtils.convertToLongArray(mDnsTimeStamp),
- mEvaluationType, mNetworkType);
+ mEvaluationType, mNetworkType, mTcpFailRate, mTcpSentSinceLastRecv);
}
}
}
diff --git a/src/com/android/networkstack/metrics/DataStallStatsUtils.java b/src/com/android/networkstack/metrics/DataStallStatsUtils.java
index e7a6c3d..c0719c0 100644
--- a/src/com/android/networkstack/metrics/DataStallStatsUtils.java
+++ b/src/com/android/networkstack/metrics/DataStallStatsUtils.java
@@ -74,6 +74,8 @@ public class DataStallStatsUtils {
stats.mNetworkType,
stats.mWifiInfo,
stats.mCellularInfo,
- stats.mDns);
+ stats.mDns,
+ stats.mTcpFailRate,
+ stats.mTcpSentSinceLastRecv);
}
}
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java
index 7fb6761..093118a 100755
--- a/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -48,6 +48,7 @@ import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INT
import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_TCP_POLLING_INTERVAL;
import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
+import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_NONE;
import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_TCP;
import static android.net.util.DataStallUtils.DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD;
import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPES;
@@ -112,6 +113,7 @@ import android.net.metrics.NetworkEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
+import android.net.util.DataStallUtils.EvaluationType;
import android.net.util.NetworkStackUtils;
import android.net.util.SharedLog;
import android.net.util.Stopwatch;
@@ -489,8 +491,8 @@ public class NetworkMonitor extends StateMachine {
@Nullable
private final DnsStallDetector mDnsStallDetector;
private long mLastProbeTime;
- // Set to true if data stall is suspected and reset to false after metrics are sent to statsd.
- private boolean mCollectDataStallMetrics;
+ // The signal causing a data stall to be suspected. Reset to 0 after metrics are sent to statsd.
+ private @EvaluationType int mDataStallTypeToCollect;
private boolean mAcceptPartialConnectivity = false;
private final EvaluationState mEvaluationState = new EvaluationState();
@@ -977,8 +979,6 @@ public class NetworkMonitor extends StateMachine {
boolean evaluateDataStall() {
if (isDataStall()) {
- // TODO: Add tcp info into metrics.
- mCollectDataStallMetrics = true;
validationLog("Suspecting data stall, reevaluate");
return true;
}
@@ -999,7 +999,8 @@ public class NetworkMonitor extends StateMachine {
}
}
- private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) {
+ private void maybeWriteDataStallStats(@NonNull final CaptivePortalProbeResult result) {
+ if (mDataStallTypeToCollect == DATA_STALL_EVALUATION_TYPE_NONE) return;
/*
* Collect data stall detection level information for each transport type. Collect type
* specific information for cellular and wifi only currently. Generate
@@ -1007,19 +1008,22 @@ public class NetworkMonitor extends StateMachine {
* TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated.
*/
final int[] transports = mNetworkCapabilities.getTransportTypes();
-
for (int i = 0; i < transports.length; i++) {
- final DataStallDetectionStats stats = buildDataStallDetectionStats(transports[i]);
+ final DataStallDetectionStats stats =
+ buildDataStallDetectionStats(transports[i], mDataStallTypeToCollect);
mDependencies.writeDataStallDetectionStats(stats, result);
}
- mCollectDataStallMetrics = false;
+ mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_NONE;
}
@VisibleForTesting
- protected DataStallDetectionStats buildDataStallDetectionStats(int transport) {
+ protected DataStallDetectionStats buildDataStallDetectionStats(int transport,
+ @EvaluationType int evaluationType) {
final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder();
- if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport);
- stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
+ if (VDBG_STALL) {
+ log("collectDataStallMetrics: type=" + transport + ", evaluation=" + evaluationType);
+ }
+ stats.setEvaluationType(evaluationType);
stats.setNetworkType(transport);
switch (transport) {
case NetworkCapabilities.TRANSPORT_WIFI:
@@ -1044,11 +1048,21 @@ public class NetworkMonitor extends StateMachine {
// No transport type specific information for the other types.
break;
}
+
addDnsEvents(stats);
+ addTcpStats(stats);
return stats.build();
}
+ private void addTcpStats(@NonNull final DataStallDetectionStats.Builder stats) {
+ final TcpSocketTracker tst = getTcpSocketTracker();
+ if (tst == null) return;
+
+ stats.setTcpSentSinceLastRecv(tst.getSentSinceLastRecv());
+ stats.setTcpFailRate(tst.getLatestPacketFailPercentage());
+ }
+
@VisibleForTesting
protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
final DnsStallDetector dsd = getDnsStallDetector();
@@ -1438,9 +1452,7 @@ public class NetworkMonitor extends StateMachine {
(CaptivePortalProbeResult) message.obj;
mLastProbeTime = SystemClock.elapsedRealtime();
- if (mCollectDataStallMetrics) {
- writeDataStallStats(probeResult);
- }
+ maybeWriteDataStallStats(probeResult);
if (probeResult.isSuccessful()) {
// Transit EvaluatingPrivateDnsState to get to Validated
@@ -1919,7 +1931,8 @@ public class NetworkMonitor extends StateMachine {
DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS);
}
- private int getDataStallEvaluationType() {
+ @VisibleForTesting
+ int getDataStallEvaluationType() {
return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
CONFIG_DATA_STALL_EVALUATION_TYPE,
DEFAULT_DATA_STALL_EVALUATION_TYPES);
@@ -3140,9 +3153,8 @@ public class NetworkMonitor extends StateMachine {
return mDnsStallDetector;
}
- @VisibleForTesting
@Nullable
- protected TcpSocketTracker getTcpSocketTracker() {
+ private TcpSocketTracker getTcpSocketTracker() {
return mTcpTracker;
}
@@ -3179,6 +3191,7 @@ public class NetworkMonitor extends StateMachine {
result = false;
} else if (tst.isDataStallSuspected()) {
result = true;
+ mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_TCP;
final DataStallReportParcelable p = new DataStallReportParcelable();
p.detectionMethod = DETECTION_METHOD_TCP_METRICS;
@@ -3203,6 +3216,7 @@ public class NetworkMonitor extends StateMachine {
if (dsd.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
mDataStallValidDnsTimeThreshold)) {
result = true;
+ mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_DNS;
logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
final DataStallReportParcelable p = new DataStallReportParcelable();
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
index dace986..4425f29 100644
--- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -39,6 +39,7 @@ import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_TCP_POLLING_INTE
import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_TCP;
+import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPES;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
@@ -142,10 +143,15 @@ import com.android.networkstack.metrics.DataStallDetectionStats;
import com.android.networkstack.metrics.DataStallStatsUtils;
import com.android.networkstack.netlink.TcpSocketTracker;
import com.android.server.NetworkStackService.NetworkStackServiceManager;
+import com.android.server.connectivity.nano.CellularData;
+import com.android.server.connectivity.nano.DnsEvent;
+import com.android.server.connectivity.nano.WifiData;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.HandlerUtilsKt;
+import com.google.protobuf.nano.MessageNano;
+
import junit.framework.AssertionFailedError;
import org.junit.After;
@@ -225,7 +231,6 @@ public class NetworkMonitorTest {
private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID);
private @Mock Network mNetwork;
private @Mock DataStallStatsUtils mDataStallStatsUtils;
- private @Mock WifiInfo mWifiInfo;
private @Mock TcpSocketTracker.Dependencies mTstDependencies;
private @Mock INetd mNetd;
private @Mock TcpSocketTracker mTst;
@@ -233,6 +238,7 @@ public class NetworkMonitorTest {
private HashSet<BroadcastReceiver> mRegisteredReceivers;
private @Mock Context mMccContext;
private @Mock Resources mMccResource;
+ private @Mock WifiInfo mWifiInfo;
private static final int TEST_NETID = 4242;
private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
@@ -250,10 +256,12 @@ public class NetworkMonitorTest {
private static final String TEST_VENUE_INFO_URL = "https://venue.example.com/info";
private static final String TEST_SPEED_TEST_URL = "https://speedtest.example.com";
private static final String TEST_MCCMNC = "123456";
-
private static final String[] TEST_HTTP_URLS = {TEST_HTTP_OTHER_URL1, TEST_HTTP_OTHER_URL2};
private static final String[] TEST_HTTPS_URLS = {TEST_HTTPS_OTHER_URL1, TEST_HTTPS_OTHER_URL2};
-
+ private static final int TEST_TCP_FAIL_RATE = 99;
+ private static final int TEST_TCP_PACKET_COUNT = 50;
+ private static final long TEST_ELAPSED_TIME_MS = 123456789L;
+ private static final int TEST_SIGNAL_STRENGTH = -100;
private static final int VALIDATION_RESULT_INVALID = 0;
private static final int VALIDATION_RESULT_PORTAL = 0;
private static final String TEST_REDIRECT_URL = "android.com";
@@ -286,6 +294,12 @@ public class NetworkMonitorTest {
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ private static final NetworkCapabilities WIFI_NOT_METERED_CAPABILITIES =
+ new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+
private static final NetworkCapabilities CELL_NO_INTERNET_CAPABILITIES =
new NetworkCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
@@ -443,6 +457,10 @@ public class NetworkMonitorTest {
when(mServiceManager.getNotifier()).thenReturn(mNotifier);
+ when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+ when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
+ when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
+
when(mResources.getString(anyInt())).thenReturn("");
when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
doReturn(mConfiguration).when(mResources).getConfiguration();
@@ -555,13 +573,21 @@ public class NetworkMonitorTest {
}
}
+ private TcpSocketTracker getTcpSocketTrackerOrNull(NetworkMonitor.Dependencies dp) {
+ return ((dp.getDeviceConfigPropertyInt(
+ NAMESPACE_CONNECTIVITY,
+ CONFIG_DATA_STALL_EVALUATION_TYPE,
+ DEFAULT_DATA_STALL_EVALUATION_TYPES)
+ & DATA_STALL_EVALUATION_TYPE_TCP) != 0) ? mTst : null;
+ }
+
private class WrappedNetworkMonitor extends NetworkMonitor {
private long mProbeTime = 0;
private final ConditionVariable mQuitCv = new ConditionVariable(false);
WrappedNetworkMonitor() {
super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, mServiceManager,
- mDependencies, mTst);
+ mDependencies, getTcpSocketTrackerOrNull(mDependencies));
}
@Override
@@ -574,13 +600,10 @@ public class NetworkMonitorTest {
}
@Override
- protected TcpSocketTracker getTcpSocketTracker() {
- return mTst;
- }
-
- @Override
protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
- generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ if ((getDataStallEvaluationType() & DATA_STALL_EVALUATION_TYPE_DNS) != 0) {
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ }
}
@Override
@@ -620,6 +643,11 @@ public class NetworkMonitorTest {
return nm;
}
+ private WrappedNetworkMonitor makeWifiNotMeteredNetworkMonitor() {
+ final WrappedNetworkMonitor nm = makeMonitor(WIFI_NOT_METERED_CAPABILITIES);
+ return nm;
+ }
+
private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) {
nm.notifyNetworkCapabilitiesChanged(nc);
HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
@@ -1726,85 +1754,284 @@ public class NetworkMonitorTest {
}
@Test
- public void testDataStall_StallDnsSuspectedAndSendMetrics() throws Exception {
- // Connect a VALID network to simulate the data stall detection because data stall
- // evaluation will only start from validated state.
- setStatus(mHttpsConnection, 204);
- WrappedNetworkMonitor wrappedMonitor = makeCellNotMeteredNetworkMonitor();
- wrappedMonitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_METERED_CAPABILITIES);
- verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID,
- NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
- // Setup dns data stall signal.
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
- assertTrue(wrappedMonitor.isDataStall());
+ public void testDataStall_StallDnsSuspectedAndSendMetricsOnCell() throws Exception {
+ testDataStall_StallDnsSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_CELLULAR,
+ CELL_METERED_CAPABILITIES);
+ }
+
+ @Test
+ public void testDataStall_StallDnsSuspectedAndSendMetricsOnWifi() throws Exception {
+ testDataStall_StallDnsSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_WIFI,
+ WIFI_NOT_METERED_CAPABILITIES);
+ }
+
+ private void testDataStall_StallDnsSuspectedAndSendMetrics(int transport,
+ NetworkCapabilities nc) throws Exception {
+ // NM suspects data stall from DNS signal and sends data stall metrics.
+ final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc);
+ makeDnsTimeoutEvent(nm, 5);
// Trigger a dns signal to start evaluate data stall and upload metrics.
- wrappedMonitor.notifyDnsResponse(RETURN_CODE_DNS_TIMEOUT);
- // Setup information to prevent null data and cause NPE during testing.
- when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
- when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
- when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
- // Verify data sent as expectation.
- final DataStallDetectionStats stats = wrappedMonitor.buildDataStallDetectionStats(
- NetworkCapabilities.TRANSPORT_CELLULAR);
- final ArgumentCaptor<CaptivePortalProbeResult> probeResultCaptor =
- ArgumentCaptor.forClass(CaptivePortalProbeResult.class);
- verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).times(1))
- .writeDataStallDetectionStats(eq(stats), probeResultCaptor.capture());
- assertTrue(probeResultCaptor.getValue().isSuccessful());
+ nm.notifyDnsResponse(RETURN_CODE_DNS_TIMEOUT);
+ // Verify data sent as expected.
+ verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_DNS, transport);
}
@Test
public void testDataStall_NoStallSuspectedAndSendMetrics() throws Exception {
+ final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(
+ CELL_METERED_CAPABILITIES);
+ // Setup no data stall dns signal.
+ makeDnsTimeoutEvent(nm, 3);
+ assertFalse(nm.isDataStall());
+ // Trigger a dns signal to start evaluate data stall.
+ nm.notifyDnsResponse(RETURN_CODE_DNS_SUCCESS);
+ verify(mDependencies, never()).writeDataStallDetectionStats(any(), any());
+ }
+
+ @Test
+ public void testDataStall_StallTcpSuspectedAndSendMetricsOnCell() throws Exception {
+ testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_CELLULAR,
+ CELL_METERED_CAPABILITIES);
+ }
+
+ @Test
+ public void testDataStall_StallTcpSuspectedAndSendMetricsOnWifi() throws Exception {
+ testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_WIFI,
+ WIFI_NOT_METERED_CAPABILITIES);
+ }
+
+ private void testDataStall_StallTcpSuspectedAndSendMetrics(int transport,
+ NetworkCapabilities nc) throws Exception {
+ assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ // NM suspects data stall from TCP signal and sends data stall metrics.
+ setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP);
+ final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc);
+ setupTcpDataStall();
+ // Trigger a tcp event immediately.
+ setTcpPollingInterval(0);
+ nm.sendTcpPollingEvent();
+ verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_TCP, transport);
+ }
+
+ private WrappedNetworkMonitor prepareNetworkMonitorForVerifyDataStall(NetworkCapabilities nc)
+ throws Exception {
// Connect a VALID network to simulate the data stall detection because data stall
// evaluation will only start from validated state.
setStatus(mHttpsConnection, 204);
- WrappedNetworkMonitor wrappedMonitor = makeCellNotMeteredNetworkMonitor();
- wrappedMonitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_METERED_CAPABILITIES);
+ final WrappedNetworkMonitor nm;
+ final int[] transports = nc.getTransportTypes();
+ // Though multiple transport types are allowed, use the first transport type for
+ // simplification.
+ switch (transports[0]) {
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ nm = makeCellMeteredNetworkMonitor();
+ break;
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ nm = makeWifiNotMeteredNetworkMonitor();
+ setupTestWifiInfo();
+ break;
+ default:
+ nm = null;
+ fail("Undefined transport type");
+ }
+ nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID,
NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
- // Setup no data stall dns signal.
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 3);
- assertFalse(wrappedMonitor.isDataStall());
- // Trigger a dns signal to start evaluate data stall.
- wrappedMonitor.notifyDnsResponse(RETURN_CODE_DNS_SUCCESS);
- verify(mDependencies, never()).writeDataStallDetectionStats(any(), any());
+ nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ return nm;
}
- @Test
- public void testCollectDataStallMetrics() {
- WrappedNetworkMonitor wrappedMonitor = makeCellNotMeteredNetworkMonitor();
+ private void setupTcpDataStall() {
+ when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true);
+ when(mTst.getLatestReceivedCount()).thenReturn(0);
+ when(mTst.getLatestPacketFailPercentage()).thenReturn(TEST_TCP_FAIL_RATE);
+ when(mTst.getSentSinceLastRecv()).thenReturn(TEST_TCP_PACKET_COUNT);
+ when(mTst.isDataStallSuspected()).thenReturn(true);
+ when(mTst.pollSocketsInfo()).thenReturn(true);
+ }
- when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
- when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
- when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
+ private void verifySendDataStallDetectionStats(WrappedNetworkMonitor nm, int evalType,
+ int transport) {
+ // Verify data sent as expectated.
+ final ArgumentCaptor<CaptivePortalProbeResult> probeResultCaptor =
+ ArgumentCaptor.forClass(CaptivePortalProbeResult.class);
+ final ArgumentCaptor<DataStallDetectionStats> statsCaptor =
+ ArgumentCaptor.forClass(DataStallDetectionStats.class);
+ verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .writeDataStallDetectionStats(statsCaptor.capture(), probeResultCaptor.capture());
+ assertTrue(nm.isDataStall());
+ assertTrue(probeResultCaptor.getValue().isSuccessful());
+ verifyTestDataStallDetectionStats(evalType, transport, statsCaptor.getValue());
+ }
+
+ private void verifyTestDataStallDetectionStats(int evalType, int transport,
+ DataStallDetectionStats stats) {
+ assertEquals(transport, stats.mNetworkType);
+ switch (transport) {
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ assertArrayEquals(makeTestWifiDataNano(), stats.mWifiInfo);
+ // Expedient way to check stats.mCellularInfo contains the neutral byte array that
+ // is sent to represent a lack of data, as stats.mCellularInfo is not supposed to
+ // contain null.
+ assertArrayEquals(DataStallDetectionStats.emptyCellDataIfNull(null),
+ stats.mCellularInfo);
+ break;
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ // Expedient way to check stats.mWifiInfo contains the neutral byte array that is
+ // sent to represent a lack of data, as stats.mWifiInfo is not supposed to contain
+ // null.
+ assertArrayEquals(DataStallDetectionStats.emptyWifiInfoIfNull(null),
+ stats.mWifiInfo);
+ assertArrayEquals(makeTestCellDataNano(), stats.mCellularInfo);
+ break;
+ default:
+ // Add other cases.
+ fail("Unexpected transport type");
+ }
+
+ assertEquals(evalType, stats.mEvaluationType);
+ if ((evalType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) {
+ assertEquals(TEST_TCP_FAIL_RATE, stats.mTcpFailRate);
+ assertEquals(TEST_TCP_PACKET_COUNT, stats.mTcpSentSinceLastRecv);
+ } else {
+ assertEquals(DataStallDetectionStats.UNSPECIFIED_TCP_FAIL_RATE, stats.mTcpFailRate);
+ assertEquals(DataStallDetectionStats.UNSPECIFIED_TCP_PACKETS_COUNT,
+ stats.mTcpSentSinceLastRecv);
+ }
+
+ if ((evalType & DATA_STALL_EVALUATION_TYPE_DNS) != 0) {
+ assertArrayEquals(stats.mDns, makeTestDnsTimeoutNano(DEFAULT_DNS_TIMEOUT_THRESHOLD));
+ } else {
+ assertArrayEquals(stats.mDns, makeTestDnsTimeoutNano(0 /* times */));
+ }
+ }
- DataStallDetectionStats.Builder stats =
- new DataStallDetectionStats.Builder()
- .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
- .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
+ private DataStallDetectionStats makeTestDataStallDetectionStats(int evaluationType,
+ int transportType) {
+ final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder()
+ .setEvaluationType(evaluationType)
+ .setNetworkType(transportType);
+ switch (transportType) {
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ stats.setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
true /* roaming */,
TEST_MCCMNC /* networkMccmnc */,
TEST_MCCMNC /* simMccmnc */,
CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */);
- generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ break;
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ setupTestWifiInfo();
+ stats.setWiFiData(mWifiInfo);
+ break;
+ default:
+ break;
+ }
+
+ if ((evaluationType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) {
+ generateTestTcpStats(stats);
+ }
+
+ if ((evaluationType & DATA_STALL_EVALUATION_TYPE_DNS) != 0) {
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ }
+
+ return stats.build();
+ }
- assertEquals(wrappedMonitor.buildDataStallDetectionStats(
- NetworkCapabilities.TRANSPORT_CELLULAR), stats.build());
+ private byte[] makeTestDnsTimeoutNano(int timeoutCount) {
+ // Make a expected nano dns message.
+ final DnsEvent event = new DnsEvent();
+ event.dnsReturnCode = new int[timeoutCount];
+ event.dnsTime = new long[timeoutCount];
+ Arrays.fill(event.dnsReturnCode, RETURN_CODE_DNS_TIMEOUT);
+ Arrays.fill(event.dnsTime, TEST_ELAPSED_TIME_MS);
+ return MessageNano.toByteArray(event);
+ }
+
+ private byte[] makeTestCellDataNano() {
+ final CellularData data = new CellularData();
+ data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_LTE;
+ data.networkMccmnc = TEST_MCCMNC;
+ data.simMccmnc = TEST_MCCMNC;
+ data.isRoaming = true;
+ data.signalStrength = 0;
+ return MessageNano.toByteArray(data);
+ }
+
+ private byte[] makeTestWifiDataNano() {
+ final WifiData data = new WifiData();
+ data.wifiBand = DataStallEventProto.AP_BAND_2GHZ;
+ data.signalStrength = TEST_SIGNAL_STRENGTH;
+ return MessageNano.toByteArray(data);
+ }
+ private void setupTestWifiInfo() {
when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
+ when(mWifiInfo.getRssi()).thenReturn(TEST_SIGNAL_STRENGTH);
+ // Set to 2.4G band. Map to DataStallEventProto.AP_BAND_2GHZ proto definition.
+ when(mWifiInfo.getFrequency()).thenReturn(2450);
+ }
- stats = new DataStallDetectionStats.Builder()
- .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
- .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI)
- .setWiFiData(mWifiInfo);
- generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ private void testDataStallMetricsWithCellular(int evalType) {
+ testDataStallMetrics(evalType, NetworkCapabilities.TRANSPORT_CELLULAR);
+ }
- assertEquals(
- wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI),
- stats.build());
+ private void testDataStallMetricsWithWiFi(int evalType) {
+ testDataStallMetrics(evalType, NetworkCapabilities.TRANSPORT_WIFI);
+ }
+
+ private void testDataStallMetrics(int evalType, int transportType) {
+ setDataStallEvaluationType(evalType);
+ final NetworkCapabilities nc = new NetworkCapabilities()
+ .addTransportType(transportType)
+ .addCapability(NET_CAPABILITY_INTERNET);
+ final WrappedNetworkMonitor wrappedMonitor = makeMonitor(nc);
+ setupTestWifiInfo();
+ final DataStallDetectionStats stats =
+ makeTestDataStallDetectionStats(evalType, transportType);
+ assertEquals(wrappedMonitor.buildDataStallDetectionStats(transportType, evalType), stats);
+
+ if ((evalType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) {
+ verify(mTst, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()).getLatestPacketFailPercentage();
+ } else {
+ verify(mTst, never()).getLatestPacketFailPercentage();
+ }
+ }
+
+ @Test
+ public void testCollectDataStallMetrics_DnsWithCellular() {
+ testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_DNS);
+ }
+
+ @Test
+ public void testCollectDataStallMetrics_DnsWithWiFi() {
+ testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_DNS);
+ }
+
+ @Test
+ public void testCollectDataStallMetrics_TcpWithCellular() {
+ assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_TCP);
+ }
+
+ @Test
+ public void testCollectDataStallMetrics_TcpWithWiFi() {
+ assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_TCP);
+ }
+
+ @Test
+ public void testCollectDataStallMetrics_TcpAndDnsWithWifi() {
+ assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ testDataStallMetricsWithWiFi(
+ DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS);
+ }
+
+ @Test
+ public void testCollectDataStallMetrics_TcpAndDnsWithCellular() {
+ assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ testDataStallMetricsWithCellular(
+ DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS);
}
@Test
@@ -2324,10 +2551,16 @@ public class NetworkMonitorTest {
private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
for (int i = 0; i < num; i++) {
- stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */);
+ stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, TEST_ELAPSED_TIME_MS /* timeMs */);
}
}
+ private void generateTestTcpStats(DataStallDetectionStats.Builder stats) {
+ when(mTst.getLatestPacketFailPercentage()).thenReturn(TEST_TCP_FAIL_RATE);
+ when(mTst.getSentSinceLastRecv()).thenReturn(TEST_TCP_PACKET_COUNT);
+ stats.setTcpFailRate(TEST_TCP_FAIL_RATE).setTcpSentSinceLastRecv(TEST_TCP_PACKET_COUNT);
+ }
+
private NetworkTestResultParcelable matchNetworkTestResultParcelable(final int result,
final int probesSucceeded) {
return matchNetworkTestResultParcelable(result, probesSucceeded, null /* redirectUrl */);