Injecting data stall event to statsd
Bug: 121185319
Bug: 120452078
Test: - Build and test against data stall code
- atest NetworkStackTests
Change-Id: I2ee16085e7861d38bcd203c98ffd682f86fdc867
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index b700bf3..5f1f26d 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -28,6 +28,7 @@
static_libs: [
"netd_aidl_interface-java",
"networkstack-aidl-interfaces-java",
+ "datastallprotosnano",
]
}
@@ -43,4 +44,4 @@
jarjar_rules: "jarjar-rules-shared.txt",
manifest: "AndroidManifest.xml",
required: ["NetworkStackPermissionStub"],
-}
\ No newline at end of file
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
new file mode 100644
index 0000000..6ea1e3d
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.util.NetworkStackUtils;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.util.HexDump;
+import com.android.server.connectivity.nano.CellularData;
+import com.android.server.connectivity.nano.DataStallEventProto;
+import com.android.server.connectivity.nano.DnsEvent;
+import com.android.server.connectivity.nano.WifiData;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to record the stats of detection level information for data stall.
+ *
+ * @hide
+ */
+public final class DataStallDetectionStats {
+ private static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+ @NonNull
+ final byte[] mCellularInfo;
+ @NonNull
+ final byte[] mWifiInfo;
+ @NonNull
+ final byte[] mDns;
+ final int mEvaluationType;
+ final int mNetworkType;
+
+ public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi,
+ @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) {
+ mCellularInfo = emptyCellDataIfNull(cell);
+ mWifiInfo = emptyWifiInfoIfNull(wifi);
+
+ DnsEvent dns = new DnsEvent();
+ dns.dnsReturnCode = returnCode;
+ dns.dnsTime = dnsTime;
+ mDns = MessageNano.toByteArray(dns);
+ mEvaluationType = evalType;
+ mNetworkType = netType;
+ }
+
+ private byte[] emptyCellDataIfNull(@Nullable byte[] cell) {
+ if (cell != null) return cell;
+
+ CellularData data = new CellularData();
+ data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN;
+ data.networkMccmnc = "";
+ data.simMccmnc = "";
+ data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
+ return MessageNano.toByteArray(data);
+ }
+
+ private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) {
+ if (wifi != null) return wifi;
+
+ WifiData data = new WifiData();
+ data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN;
+ data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
+ return MessageNano.toByteArray(data);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("type: ").append(mNetworkType)
+ .append(", evaluation type: ")
+ .append(mEvaluationType)
+ .append(", wifi info: ")
+ .append(HexDump.toHexString(mWifiInfo))
+ .append(", cell info: ")
+ .append(HexDump.toHexString(mCellularInfo))
+ .append(", dns: ")
+ .append(HexDump.toHexString(mDns));
+ return sb.toString();
+ }
+
+ /**
+ * Utility to create an instance of {@Link DataStallDetectionStats}
+ *
+ * @hide
+ */
+ public static class Builder {
+ @Nullable
+ private byte[] mCellularInfo;
+ @Nullable
+ private byte[] mWifiInfo;
+ @NonNull
+ private final List<Integer> mDnsReturnCode = new ArrayList<Integer>();
+ @NonNull
+ private final List<Long> mDnsTimeStamp = new ArrayList<Long>();
+ private int mEvaluationType;
+ private int mNetworkType;
+
+ /**
+ * Add a dns event into Builder.
+ *
+ * @param code the return code of the dns event.
+ * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder addDnsEvent(int code, long timeMs) {
+ mDnsReturnCode.add(code);
+ mDnsTimeStamp.add(timeMs);
+ return this;
+ }
+
+ /**
+ * Set the dns evaluation type into Builder.
+ *
+ * @param type the return code of the dns event.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setEvaluationType(int type) {
+ mEvaluationType = type;
+ return this;
+ }
+
+ /**
+ * Set the network type into Builder.
+ *
+ * @param type the network type of the logged network.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setNetworkType(int type) {
+ mNetworkType = type;
+ return this;
+ }
+
+ /**
+ * Set the wifi data into Builder.
+ *
+ * @param info a {@link WifiInfo} of the connected wifi network.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setWiFiData(@Nullable final WifiInfo info) {
+ WifiData data = new WifiData();
+ data.wifiBand = getWifiBand(info);
+ data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH;
+ mWifiInfo = MessageNano.toByteArray(data);
+ return this;
+ }
+
+ private static int getWifiBand(@Nullable final WifiInfo info) {
+ if (info != null) {
+ int freq = info.getFrequency();
+ // Refer to ScanResult.is5GHz() and ScanResult.is24GHz().
+ if (freq > 4900 && freq < 5900) {
+ return DataStallEventProto.AP_BAND_5GHZ;
+ } else if (freq > 2400 && freq < 2500) {
+ return DataStallEventProto.AP_BAND_2GHZ;
+ }
+ }
+ return DataStallEventProto.AP_BAND_UNKNOWN;
+ }
+
+ /**
+ * Set the cellular data into Builder.
+ *
+ * @param radioType the radio technology of the logged cellular network.
+ * @param roaming a boolean indicates if logged cellular network is roaming or not.
+ * @param networkMccmnc the mccmnc of the camped network.
+ * @param simMccmnc the mccmnc of the sim.
+ * @return {@code this} {@link Builder} instance.
+ */
+ public Builder setCellData(int radioType, boolean roaming,
+ @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) {
+ CellularData data = new CellularData();
+ data.ratType = radioType;
+ data.isRoaming = roaming;
+ data.networkMccmnc = networkMccmnc;
+ data.simMccmnc = simMccmnc;
+ data.signalStrength = ss;
+ mCellularInfo = MessageNano.toByteArray(data);
+ return this;
+ }
+
+ /**
+ * Create a new {@Link DataStallDetectionStats}.
+ */
+ public DataStallDetectionStats build() {
+ return new DataStallDetectionStats(mCellularInfo, mWifiInfo,
+ NetworkStackUtils.convertToIntArray(mDnsReturnCode),
+ NetworkStackUtils.convertToLongArray(mDnsTimeStamp),
+ mEvaluationType, mNetworkType);
+ }
+ }
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
new file mode 100644
index 0000000..17a36ad
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.captiveportal.CaptivePortalProbeResult;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+import com.android.server.connectivity.nano.DataStallEventProto;
+
+/**
+ * Collection of utilities for data stall metrics.
+ *
+ * To see if the logs are properly sent to statsd, execute following command.
+ *
+ * $ adb shell cmd stats print-logs
+ * $ adb logcat | grep statsd OR $ adb logcat -b stats
+ *
+ * @hide
+ */
+public class DataStallStatsUtils {
+ private static final String TAG = DataStallStatsUtils.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) {
+ if (result == null) return DataStallEventProto.INVALID;
+
+ // TODO: Add partial connectivity support.
+ if (result.isSuccessful()) {
+ return DataStallEventProto.VALID;
+ } else if (result.isPortal()) {
+ return DataStallEventProto.PORTAL;
+ } else {
+ return DataStallEventProto.INVALID;
+ }
+ }
+
+ /**
+ * Write the metric to {@link StatsLog}.
+ */
+ public static void write(@NonNull final DataStallDetectionStats stats,
+ @NonNull final CaptivePortalProbeResult result) {
+ int validationResult = probeResultToEnum(result);
+ if (DBG) {
+ Log.d(TAG, "write: " + stats + " with result: " + validationResult
+ + ", dns: " + HexDump.toHexString(stats.mDns));
+ }
+ // TODO(b/124613085): Send to Statsd once the public StatsLog API is ready.
+ }
+}
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 98123a5..481dbda 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -16,8 +16,11 @@
package android.net.util;
+import android.annotation.NonNull;
+
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.List;
/**
* Collection of utilities for the network stack.
@@ -40,4 +43,26 @@
} catch (IOException ignored) {
}
}
+
+ /**
+ * Returns an int array from the given Integer list.
+ */
+ public static int[] convertToIntArray(@NonNull List<Integer> list) {
+ int[] array = new int[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i);
+ }
+ return array;
+ }
+
+ /**
+ * Returns a long array from the given long list.
+ */
+ public static long[] convertToLongArray(@NonNull List<Long> list) {
+ long[] array = new long[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i);
+ }
+ return array;
+ }
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index ec4a479..1c803dd 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -33,6 +33,7 @@
import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS;
import static android.net.util.NetworkStackUtils.isEmpty;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -50,6 +51,8 @@
import android.net.Uri;
import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.captiveportal.CaptivePortalProbeSpec;
+import android.net.metrics.DataStallDetectionStats;
+import android.net.metrics.DataStallStatsUtils;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.metrics.ValidationProbeEvent;
@@ -66,8 +69,10 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
+import android.telephony.CellSignalStrength;
import android.telephony.NetworkRegistrationState;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -126,6 +131,9 @@
private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
(1 << DATA_STALL_EVALUATION_TYPE_DNS);
+ // Reevaluate it as intending to increase the number. Larger log size may cause statsd
+ // log buffer bust and have stats log lost.
+ private static final int DEFAULT_DNS_LOG_SIZE = 20;
enum EvaluationResult {
VALIDATED(true),
@@ -244,6 +252,7 @@
private final ConnectivityManager mCm;
private final IpConnectivityLog mMetricsLog;
private final Dependencies mDependencies;
+ private final DataStallStatsUtils mDetectionStatsUtils;
// Configuration values for captive portal detection probes.
private final String mCaptivePortalUserAgent;
@@ -302,17 +311,19 @@
private final int mDataStallEvaluationType;
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 = false;
public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
SharedLog validationLog) {
this(context, cb, network, new IpConnectivityLog(), validationLog,
- Dependencies.DEFAULT);
+ Dependencies.DEFAULT, new DataStallStatsUtils());
}
@VisibleForTesting
protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
IpConnectivityLog logger, SharedLog validationLogs,
- Dependencies deps) {
+ Dependencies deps, DataStallStatsUtils detectionStatsUtils) {
// Add suffix indicating which NetworkMonitor we're talking about.
super(TAG + "/" + network.toString());
@@ -325,6 +336,7 @@
mValidationLogs = validationLogs;
mCallback = cb;
mDependencies = deps;
+ mDetectionStatsUtils = detectionStatsUtils;
mNonPrivateDnsBypassNetwork = network;
mNetwork = deps.getPrivateDnsBypassNetwork(network);
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -656,6 +668,7 @@
case EVENT_DNS_NOTIFICATION:
mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
if (isDataStall()) {
+ mCollectDataStallMetrics = true;
validationLog("Suspecting data stall, reevaluate");
transitionTo(mEvaluatingState);
}
@@ -667,6 +680,65 @@
}
}
+ private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) {
+ /*
+ * Collect data stall detection level information for each transport type. Collect type
+ * specific information for cellular and wifi only currently. Generate
+ * DataStallDetectionStats for each transport type. E.g., if a network supports both
+ * TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated.
+ */
+ final int[] transports = mNetworkCapabilities.getTransportTypes();
+
+ for (int i = 0; i < transports.length; i++) {
+ DataStallStatsUtils.write(buildDataStallDetectionStats(transports[i]), result);
+ }
+ mCollectDataStallMetrics = false;
+ }
+
+ @VisibleForTesting
+ protected DataStallDetectionStats buildDataStallDetectionStats(int transport) {
+ final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder();
+ if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport);
+ stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
+ stats.setNetworkType(transport);
+ switch (transport) {
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ // TODO: Update it if status query in dual wifi is supported.
+ final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ stats.setWiFiData(wifiInfo);
+ break;
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ final boolean isRoaming = !mNetworkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+ final SignalStrength ss = mTelephonyManager.getSignalStrength();
+ // TODO(b/120452078): Support multi-sim.
+ stats.setCellData(
+ mTelephonyManager.getDataNetworkType(),
+ isRoaming,
+ mTelephonyManager.getNetworkOperator(),
+ mTelephonyManager.getSimOperator(),
+ (ss != null)
+ ? ss.getLevel() : CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ break;
+ default:
+ // No transport type specific information for the other types.
+ break;
+ }
+ addDnsEvents(stats);
+
+ return stats.build();
+ }
+
+ private void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
+ final int size = mDnsStallDetector.mResultIndices.size();
+ for (int i = 1; i <= DEFAULT_DNS_LOG_SIZE && i <= size; i++) {
+ final int index = mDnsStallDetector.mResultIndices.indexOf(size - i);
+ stats.addDnsEvent(mDnsStallDetector.mDnsEvents[index].mReturnCode,
+ mDnsStallDetector.mDnsEvents[index].mTimeStamp);
+ }
+ }
+
+
// Being in the MaybeNotifyState State indicates the user may have been notified that sign-in
// is required. This State takes care to clear the notification upon exit from the State.
private class MaybeNotifyState extends State {
@@ -972,6 +1044,11 @@
final CaptivePortalProbeResult probeResult =
(CaptivePortalProbeResult) message.obj;
mLastProbeTime = SystemClock.elapsedRealtime();
+
+ if (mCollectDataStallMetrics) {
+ writeDataStallStats(probeResult);
+ }
+
if (probeResult.isSuccessful()) {
// Transit EvaluatingPrivateDnsState to get to Validated
// state (even if no Private DNS validation required).
@@ -1615,7 +1692,6 @@
*/
@VisibleForTesting
protected class DnsStallDetector {
- private static final int DEFAULT_DNS_LOG_SIZE = 50;
private int mConsecutiveTimeoutCount = 0;
private int mSize;
final DnsResult[] mDnsEvents;
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 9a16bb7..beb975d 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -48,6 +49,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.captiveportal.CaptivePortalProbeResult;
+import android.net.metrics.DataStallStatsUtils;
import android.net.metrics.IpConnectivityLog;
import android.net.util.SharedLog;
import android.net.wifi.WifiManager;
@@ -98,6 +100,7 @@
private @Mock NetworkMonitor.Dependencies mDependencies;
private @Mock INetworkMonitorCallbacks mCallbacks;
private @Spy Network mNetwork = new Network(TEST_NETID);
+ private @Mock DataStallStatsUtils mDataStallStatsUtils;
private static final int TEST_NETID = 4242;
@@ -186,9 +189,9 @@
private long mProbeTime = 0;
WrappedNetworkMonitor(Context context, Network network, IpConnectivityLog logger,
- Dependencies deps) {
+ Dependencies deps, DataStallStatsUtils statsUtils) {
super(context, mCallbacks, network, logger,
- new SharedLog("test_nm"), deps);
+ new SharedLog("test_nm"), deps, statsUtils);
}
@Override
@@ -203,7 +206,7 @@
private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
- mContext, mNetwork, mLogger, mDependencies);
+ mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
nm.start();
waitForIdle(nm.getHandler());
@@ -212,7 +215,7 @@
private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
- mContext, mNetwork, mLogger, mDependencies);
+ mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES);
nm.start();
waitForIdle(nm.getHandler());
@@ -222,7 +225,7 @@
private NetworkMonitor makeMonitor() {
final NetworkMonitor nm = new NetworkMonitor(
mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
- mDependencies);
+ mDependencies, mDataStallStatsUtils);
nm.start();
waitForIdle(nm.getHandler());
return nm;
@@ -505,6 +508,23 @@
.notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
}
+ @Test
+ public void testDataStall_StallSuspectedAndSendMetrics() throws IOException {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertTrue(wrappedMonitor.isDataStall());
+ verify(mDataStallStatsUtils, times(1)).write(eq(anyObject()), eq(anyObject()));
+ }
+
+ @Test
+ public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertFalse(wrappedMonitor.isDataStall());
+ verify(mDataStallStatsUtils, times(0)).write(eq(anyObject()), eq(anyObject()));
+ }
private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
for (int i = 0; i < count; i++) {
wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(