Merge "Set NetworkStack targetSdk to 28"
diff --git a/Android.bp b/Android.bp
index b700bf3..5f1f26d 100644
--- a/Android.bp
+++ b/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/src/android/net/metrics/DataStallDetectionStats.java b/src/android/net/metrics/DataStallDetectionStats.java
new file mode 100644
index 0000000..225dc0f
--- /dev/null
+++ b/src/android/net/metrics/DataStallDetectionStats.java
@@ -0,0 +1,228 @@
+/*
+ * 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.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 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();
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof DataStallDetectionStats)) return false;
+ final DataStallDetectionStats other = (DataStallDetectionStats) o;
+ return (mNetworkType == other.mNetworkType)
+ && (mEvaluationType == other.mEvaluationType)
+ && Arrays.equals(mWifiInfo, other.mWifiInfo)
+ && Arrays.equals(mCellularInfo, other.mCellularInfo)
+ && Arrays.equals(mDns, other.mDns);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns);
+ }
+
+ /**
+ * 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) return DataStallEventProto.AP_BAND_UNKNOWN;
+
+ 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;
+ } else {
+ 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/src/android/net/metrics/DataStallStatsUtils.java b/src/android/net/metrics/DataStallStatsUtils.java
new file mode 100644
index 0000000..17a36ad
--- /dev/null
+++ b/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/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java
index 98123a5..481dbda 100644
--- a/src/android/net/util/NetworkStackUtils.java
+++ b/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/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java
index ec4a479..e82a5d7 100644
--- a/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/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,66 @@
}
}
+ 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();
+ }
+
+ @VisibleForTesting
+ protected 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 +1045,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).
@@ -1318,26 +1396,28 @@
// is needed (i.e. can't browse a 204). This could be the result of an HTTP
// proxy server.
if (httpResponseCode == 200) {
+ long contentLength = urlConnection.getContentLengthLong();
if (probeType == ValidationProbeEvent.PROBE_PAC) {
validationLog(
probeType, url, "PAC fetch 200 response interpreted as 204 response.");
httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
- } else if (urlConnection.getContentLengthLong() == 0) {
- // Consider 200 response with "Content-length=0" to not be a captive portal.
- // There's no point in considering this a captive portal as the user cannot
- // sign-in to an empty page. Probably the result of a broken transparent proxy.
- // See http://b/9972012.
- validationLog(probeType, url,
- "200 response with Content-length=0 interpreted as 204 response.");
- httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
- } else if (urlConnection.getContentLengthLong() == -1) {
- // When no Content-length (default value == -1), attempt to read a byte from the
- // response. Do not use available() as it is unreliable. See http://b/33498325.
+ } else if (contentLength == -1) {
+ // When no Content-length (default value == -1), attempt to read a byte
+ // from the response. Do not use available() as it is unreliable.
+ // See http://b/33498325.
if (urlConnection.getInputStream().read() == -1) {
- validationLog(
- probeType, url, "Empty 200 response interpreted as 204 response.");
- httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
+ validationLog(probeType, url,
+ "Empty 200 response interpreted as failed response.");
+ httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
}
+ } else if (contentLength <= 4) {
+ // Consider 200 response with "Content-length <= 4" to not be a captive
+ // portal. There's no point in considering this a captive portal as the
+ // user cannot sign-in to an empty page. Probably the result of a broken
+ // transparent proxy. See http://b/9972012 and http://b/122999481.
+ validationLog(probeType, url, "200 response with Content-length <= 4"
+ + " interpreted as failed response.");
+ httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
}
}
} catch (IOException e) {
@@ -1615,7 +1695,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/tests/src/android/net/apf/ApfTest.java b/tests/src/android/net/apf/ApfTest.java
index af71ac5..3414397 100644
--- a/tests/src/android/net/apf/ApfTest.java
+++ b/tests/src/android/net/apf/ApfTest.java
@@ -40,9 +40,7 @@
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.SocketKeepalive;
-import android.net.TcpKeepalivePacketData;
-import android.net.TcpKeepalivePacketData.TcpSocketInfo;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
@@ -1546,12 +1544,15 @@
InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
- final TcpSocketInfo v4Tsi = new TcpSocketInfo(
- srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
- final TcpKeepalivePacketData ipv4TcpKeepalivePacket =
- TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi);
+ final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+ parcel.srcAddress = srcAddr.getAddress();
+ parcel.srcPort = srcPort;
+ parcel.dstAddress = dstAddr.getAddress();
+ parcel.dstPort = dstPort;
+ parcel.seq = seqNum;
+ parcel.ack = ackNum;
- apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
+ apfFilter.addKeepalivePacketFilter(slot1, parcel);
program = cb.getApfProgram();
// Verify IPv4 keepalive ack packet is dropped
@@ -1580,11 +1581,17 @@
// dst: 2404:0:0:0:0:0:faf2, port: 54321
srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
- final TcpSocketInfo v6Tsi = new TcpSocketInfo(
- srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
- final TcpKeepalivePacketData ipv6TcpKeepalivePacket =
- TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi);
- apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable());
+
+ final TcpKeepalivePacketDataParcelable ipv6Parcel =
+ new TcpKeepalivePacketDataParcelable();
+ ipv6Parcel.srcAddress = srcAddr.getAddress();
+ ipv6Parcel.srcPort = srcPort;
+ ipv6Parcel.dstAddress = dstAddr.getAddress();
+ ipv6Parcel.dstPort = dstPort;
+ ipv6Parcel.seq = seqNum;
+ ipv6Parcel.ack = ackNum;
+
+ apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel);
program = cb.getApfProgram();
// Verify IPv6 keepalive ack packet is dropped
@@ -1606,8 +1613,8 @@
apfFilter.removeKeepalivePacketFilter(slot1);
// Verify multiple filters
- apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
- apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable());
+ apfFilter.addKeepalivePacketFilter(slot1, parcel);
+ apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel);
program = cb.getApfProgram();
// Verify IPv4 keepalive ack packet is dropped
@@ -1643,7 +1650,7 @@
// Remove keepalive filters
apfFilter.removeKeepalivePacketFilter(slot1);
apfFilter.removeKeepalivePacketFilter(slot2);
- } catch (SocketKeepalive.InvalidPacketException e) {
+ } catch (UnsupportedOperationException e) {
// TODO: support V6 packets
}
diff --git a/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 9a16bb7..ddb7030 100644
--- a/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -39,6 +39,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetworkMonitorCallbacks;
@@ -48,8 +49,11 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.captiveportal.CaptivePortalProbeResult;
+import android.net.metrics.DataStallDetectionStats;
+import android.net.metrics.DataStallStatsUtils;
import android.net.metrics.IpConnectivityLog;
import android.net.util.SharedLog;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.ConditionVariable;
@@ -58,6 +62,7 @@
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CellSignalStrength;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
@@ -98,6 +103,8 @@
private @Mock NetworkMonitor.Dependencies mDependencies;
private @Mock INetworkMonitorCallbacks mCallbacks;
private @Spy Network mNetwork = new Network(TEST_NETID);
+ private @Mock DataStallStatsUtils mDataStallStatsUtils;
+ private @Mock WifiInfo mWifiInfo;
private static final int TEST_NETID = 4242;
@@ -105,10 +112,12 @@
private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
+ private static final String TEST_MCCMNC = "123456";
private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
private static final int RETURN_CODE_DNS_SUCCESS = 0;
private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+ private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5;
private static final int HANDLER_TIMEOUT_MS = 1000;
@@ -186,9 +195,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
@@ -199,11 +208,16 @@
protected void setLastProbeTime(long time) {
mProbeTime = time;
}
+
+ @Override
+ protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ }
}
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 +226,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 +236,7 @@
private NetworkMonitor makeMonitor() {
final NetworkMonitor nm = new NetworkMonitor(
mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
- mDependencies);
+ mDependencies, mDataStallStatsUtils);
nm.start();
waitForIdle(nm.getHandler());
return nm;
@@ -384,7 +398,7 @@
public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertTrue(wrappedMonitor.isDataStall());
}
@@ -395,7 +409,7 @@
assertFalse(wrappedMonitor.isDataStall());
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertTrue(wrappedMonitor.isDataStall());
}
@@ -429,7 +443,7 @@
// Test dns events happened in valid dns time threshold.
WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertFalse(wrappedMonitor.isDataStall());
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
assertTrue(wrappedMonitor.isDataStall());
@@ -438,7 +452,7 @@
setValidDataStallDnsTimeThreshold(0);
wrappedMonitor = makeMeteredWrappedNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
+ makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertFalse(wrappedMonitor.isDataStall());
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
assertFalse(wrappedMonitor.isDataStall());
@@ -505,6 +519,59 @@
.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(any(), any());
+ }
+
+ @Test
+ public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertFalse(wrappedMonitor.isDataStall());
+ verify(mDataStallStatsUtils, never()).write(any(), any());
+ }
+
+ @Test
+ public void testCollectDataStallMetrics() {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+
+ when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+ when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
+ when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
+
+ DataStallDetectionStats.Builder stats =
+ new DataStallDetectionStats.Builder()
+ .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
+ .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .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);
+
+ assertEquals(wrappedMonitor.buildDataStallDetectionStats(
+ NetworkCapabilities.TRANSPORT_CELLULAR), stats.build());
+
+ when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
+
+ stats = new DataStallDetectionStats.Builder()
+ .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
+ .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setWiFiData(mWifiInfo);
+ generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+
+ assertEquals(
+ wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI),
+ stats.build());
+ }
+
private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
for (int i = 0; i < count; i++) {
wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -594,5 +661,11 @@
private void setStatus(HttpURLConnection connection, int status) throws IOException {
doReturn(status).when(connection).getResponseCode();
}
+
+ private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
+ for (int i = 0; i < num; i++) {
+ stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */);
+ }
+ }
}