Loading packages/NetworkStack/Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ java_library { static_libs: [ "netd_aidl_interface-java", "networkstack-aidl-interfaces-java", "datastallprotosnano", ] } Loading packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java 0 → 100644 +209 −0 Original line number Diff line number Diff line /* * 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); } } } packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java 0 → 100644 +66 −0 Original line number Diff line number Diff line /* * 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. } } packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +25 −0 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -40,4 +43,26 @@ public class NetworkStackUtils { } 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; } } packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +79 −3 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; 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; Loading @@ -50,6 +51,8 @@ import android.net.TrafficStats; 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; Loading @@ -66,8 +69,10 @@ import android.os.SystemClock; 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; Loading Loading @@ -126,6 +131,9 @@ public class NetworkMonitor extends StateMachine { 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), Loading Loading @@ -244,6 +252,7 @@ public class NetworkMonitor extends StateMachine { 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; Loading Loading @@ -302,17 +311,19 @@ public class NetworkMonitor extends StateMachine { 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()); Loading @@ -325,6 +336,7 @@ public class NetworkMonitor extends StateMachine { mValidationLogs = validationLogs; mCallback = cb; mDependencies = deps; mDetectionStatsUtils = detectionStatsUtils; mNonPrivateDnsBypassNetwork = network; mNetwork = deps.getPrivateDnsBypassNetwork(network); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); Loading Loading @@ -656,6 +668,7 @@ public class NetworkMonitor extends StateMachine { case EVENT_DNS_NOTIFICATION: mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1); if (isDataStall()) { mCollectDataStallMetrics = true; validationLog("Suspecting data stall, reevaluate"); transitionTo(mEvaluatingState); } Loading @@ -667,6 +680,65 @@ public class NetworkMonitor extends StateMachine { } } 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 { Loading Loading @@ -972,6 +1044,11 @@ public class NetworkMonitor extends StateMachine { 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). Loading Loading @@ -1617,7 +1694,6 @@ public class NetworkMonitor extends StateMachine { */ @VisibleForTesting protected class DnsStallDetector { private static final int DEFAULT_DNS_LOG_SIZE = 50; private int mConsecutiveTimeoutCount = 0; private int mSize; final DnsResult[] mDnsEvents; Loading Loading
packages/NetworkStack/Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ java_library { static_libs: [ "netd_aidl_interface-java", "networkstack-aidl-interfaces-java", "datastallprotosnano", ] } Loading
packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java 0 → 100644 +209 −0 Original line number Diff line number Diff line /* * 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); } } }
packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java 0 → 100644 +66 −0 Original line number Diff line number Diff line /* * 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. } }
packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +25 −0 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -40,4 +43,26 @@ public class NetworkStackUtils { } 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; } }
packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +79 −3 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; 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; Loading @@ -50,6 +51,8 @@ import android.net.TrafficStats; 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; Loading @@ -66,8 +69,10 @@ import android.os.SystemClock; 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; Loading Loading @@ -126,6 +131,9 @@ public class NetworkMonitor extends StateMachine { 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), Loading Loading @@ -244,6 +252,7 @@ public class NetworkMonitor extends StateMachine { 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; Loading Loading @@ -302,17 +311,19 @@ public class NetworkMonitor extends StateMachine { 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()); Loading @@ -325,6 +336,7 @@ public class NetworkMonitor extends StateMachine { mValidationLogs = validationLogs; mCallback = cb; mDependencies = deps; mDetectionStatsUtils = detectionStatsUtils; mNonPrivateDnsBypassNetwork = network; mNetwork = deps.getPrivateDnsBypassNetwork(network); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); Loading Loading @@ -656,6 +668,7 @@ public class NetworkMonitor extends StateMachine { case EVENT_DNS_NOTIFICATION: mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1); if (isDataStall()) { mCollectDataStallMetrics = true; validationLog("Suspecting data stall, reevaluate"); transitionTo(mEvaluatingState); } Loading @@ -667,6 +680,65 @@ public class NetworkMonitor extends StateMachine { } } 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 { Loading Loading @@ -972,6 +1044,11 @@ public class NetworkMonitor extends StateMachine { 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). Loading Loading @@ -1617,7 +1694,6 @@ public class NetworkMonitor extends StateMachine { */ @VisibleForTesting protected class DnsStallDetector { private static final int DEFAULT_DNS_LOG_SIZE = 50; private int mConsecutiveTimeoutCount = 0; private int mSize; final DnsResult[] mDnsEvents; Loading