Loading AndroidManifest.xml +2 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" android:sharedUserId="android.uid.networkstack" android:versionCode="300900400" android:versionName="r_aml_300900400" android:versionCode="300900500" android:versionName="r_aml_300900500" > <!-- Permissions must be defined here, and not in the base manifest, as the network stack running in the system server process does not need any permission, and having privileged Loading apishim/30/com/android/networkstack/apishim/api30/CaptivePortalDataShimImpl.java +10 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,16 @@ public class CaptivePortalDataShimImpl return mData.isCaptive(); } @Override public long getByteLimit() { return mData.getByteLimit(); } @Override public long getExpiryTimeMillis() { return mData.getExpiryTimeMillis(); } @Override public Uri getUserPortalUrl() { return mData.getUserPortalUrl(); Loading apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java +10 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,16 @@ public interface CaptivePortalDataShim { */ boolean isCaptive(); /** * @see android.net.CaptivePortalData#getByteLimit() */ long getByteLimit(); /** * @see android.net.CaptivePortalData#getExpiryTimeMillis() */ long getExpiryTimeMillis(); /** * @see android.net.CaptivePortalData#getUserPortalUrl() */ Loading src/android/net/util/NetworkStackUtils.java +15 −0 Original line number Diff line number Diff line Loading @@ -226,6 +226,15 @@ public class NetworkStackUtils { public static final String DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION = "dns_probe_private_ip_no_internet"; /** * Experiment flag to enable validation metrics sent by NetworkMonitor. * * Metrics are sent by default. They can be disabled by setting the flag to a number greater * than the APK version (for example 999999999). * @see #isFeatureEnabled(Context, String, String, boolean) */ public static final String VALIDATION_METRICS_VERSION = "validation_metrics_version"; static { System.loadLibrary("networkstackutilsjni"); } Loading Loading @@ -348,6 +357,9 @@ public class NetworkStackUtils { * {@link DeviceConfig} is enabled by comparing NetworkStack module version {@link NetworkStack} * with current version of property. If this property version is valid, the corresponding * experimental feature would be enabled, otherwise disabled. * * This is useful to ensure that if a module install is rolled back, flags are not left fully * rolled out on a version where they have not been well tested. * @param context The global context information about an app environment. * @param namespace The namespace containing the property to look up. * @param name The name of the property to look up. Loading @@ -363,6 +375,9 @@ public class NetworkStackUtils { * {@link DeviceConfig} is enabled by comparing NetworkStack module version {@link NetworkStack} * with current version of property. If this property version is valid, the corresponding * experimental feature would be enabled, otherwise disabled. * * This is useful to ensure that if a module install is rolled back, flags are not left fully * rolled out on a version where they have not been well tested. * @param context The global context information about an app environment. * @param namespace The namespace containing the property to look up. * @param name The name of the property to look up. Loading src/com/android/networkstack/metrics/NetworkValidationMetrics.java 0 → 100644 +254 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 com.android.networkstack.metrics; import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; import static java.lang.System.currentTimeMillis; import android.net.INetworkMonitor; import android.net.NetworkCapabilities; import android.net.captiveportal.CaptivePortalProbeResult; import android.net.metrics.ValidationProbeEvent; import android.net.util.NetworkStackUtils; import android.net.util.Stopwatch; import android.stats.connectivity.ProbeResult; import android.stats.connectivity.ProbeType; import android.stats.connectivity.TransportType; import android.stats.connectivity.ValidationResult; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.networkstack.apishim.common.CaptivePortalDataShim; /** * Class to record the network validation into statsd. * 1. Fill in NetworkValidationReported proto. * 2. Write the NetworkValidationReported proto into statsd. * @hide */ public class NetworkValidationMetrics { private final NetworkValidationReported.Builder mStatsBuilder = NetworkValidationReported.newBuilder(); private final ProbeEvents.Builder mProbeEventsBuilder = ProbeEvents.newBuilder(); private final CapportApiData.Builder mCapportApiDataBuilder = CapportApiData.newBuilder(); private final Stopwatch mWatch = new Stopwatch(); private int mValidationIndex = 0; // Define a maximum size that can store events. public static final int MAX_PROBE_EVENTS_COUNT = 20; /** * Reset this NetworkValidationMetrics and start collecting timing and metrics. * * <p>This must be called when validation starts. */ public void startCollection(@Nullable NetworkCapabilities nc) { mStatsBuilder.clear(); mProbeEventsBuilder.clear(); mCapportApiDataBuilder.clear(); mWatch.restart(); mStatsBuilder.setTransportType(getTransportTypeFromNC(nc)); mValidationIndex++; } /** * Returns the enum TransportType. * * <p>This method only supports a limited set of common transport type combinations that can be * measured through metrics, and will return {@link TransportType#TT_UNKNOWN} for others. This * ensures that, for example, metrics for a TRANSPORT_NEW_UNKNOWN | TRANSPORT_ETHERNET network * cannot get aggregated with / compared with a "normal" TRANSPORT_ETHERNET network without * noticing. * * @param nc Capabilities to extract transport type from. * @return the TransportType which is defined in * core/proto/android/stats/connectivity/network_stack.proto */ @VisibleForTesting public static TransportType getTransportTypeFromNC(@Nullable NetworkCapabilities nc) { if (nc == null) return TransportType.TT_UNKNOWN; final int trCount = nc.getTransportTypes().length; boolean hasCellular = nc.hasTransport(TRANSPORT_CELLULAR); boolean hasWifi = nc.hasTransport(TRANSPORT_WIFI); boolean hasBT = nc.hasTransport(TRANSPORT_BLUETOOTH); boolean hasEthernet = nc.hasTransport(TRANSPORT_ETHERNET); boolean hasVpn = nc.hasTransport(TRANSPORT_VPN); boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE); boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN); // VPN networks are not subject to validation and should not see validation stats, but // metrics could be added to measure private DNS probes only. if (trCount == 3 && hasCellular && hasWifi && hasVpn) { return TransportType.TT_WIFI_CELLULAR_VPN; } if (trCount == 2 && hasVpn) { if (hasWifi) return TransportType.TT_WIFI_VPN; if (hasCellular) return TransportType.TT_CELLULAR_VPN; if (hasBT) return TransportType.TT_BLUETOOTH_VPN; if (hasEthernet) return TransportType.TT_ETHERNET_VPN; } if (trCount == 1) { if (hasWifi) return TransportType.TT_WIFI; if (hasCellular) return TransportType.TT_CELLULAR; if (hasBT) return TransportType.TT_BLUETOOTH; if (hasEthernet) return TransportType.TT_ETHERNET; if (hasWifiAware) return TransportType.TT_WIFI_AWARE; if (hasLopan) return TransportType.TT_LOWPAN; // TODO: consider having a TT_VPN for VPN-only transport } return TransportType.TT_UNKNOWN; } /** * Map {@link ValidationProbeEvent} to {@link ProbeType}. */ public static ProbeType probeTypeToEnum(final int probeType) { switch(probeType) { case ValidationProbeEvent.PROBE_DNS: return ProbeType.PT_DNS; case ValidationProbeEvent.PROBE_HTTP: return ProbeType.PT_HTTP; case ValidationProbeEvent.PROBE_HTTPS: return ProbeType.PT_HTTPS; case ValidationProbeEvent.PROBE_PAC: return ProbeType.PT_PAC; case ValidationProbeEvent.PROBE_FALLBACK: return ProbeType.PT_FALLBACK; case ValidationProbeEvent.PROBE_PRIVDNS: return ProbeType.PT_PRIVDNS; default: return ProbeType.PT_UNKNOWN; } } /** * Map {@link CaptivePortalProbeResult} to {@link ProbeResult}. */ public static ProbeResult httpProbeResultToEnum(final CaptivePortalProbeResult result) { if (result == null) return ProbeResult.PR_UNKNOWN; if (result.isSuccessful()) { return ProbeResult.PR_SUCCESS; } else if (result.isDnsPrivateIpResponse()) { return ProbeResult.PR_PRIVATE_IP_DNS; } else if (result.isFailed()) { return ProbeResult.PR_FAILURE; } else if (result.isPortal()) { return ProbeResult.PR_PORTAL; } else { return ProbeResult.PR_UNKNOWN; } } /** * Map validation result (as per INetworkMonitor) to {@link ValidationResult}. */ @VisibleForTesting public static ValidationResult validationResultToEnum(int result, String redirectUrl) { // TODO: consider adding a VR_PARTIAL_SUCCESS field to track cases where users accepted // partial connectivity if ((result & INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID) != 0) { return ValidationResult.VR_SUCCESS; } else if (redirectUrl != null) { return ValidationResult.VR_PORTAL; } else if ((result & INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL) != 0) { return ValidationResult.VR_PARTIAL; } else { return ValidationResult.VR_FAILURE; } } /** * Add a network probe event to the metrics builder. */ public void addProbeEvent(final ProbeType type, final long durationUs, final ProbeResult result, @Nullable final CaptivePortalDataShim capportData) { // When the number of ProbeEvents of mProbeEventsBuilder exceeds // MAX_PROBE_EVENTS_COUNT, stop adding ProbeEvent. // TODO: consider recording the total number of probes in a separate field to know how // many probes are skipped. if (mProbeEventsBuilder.getProbeEventCount() >= MAX_PROBE_EVENTS_COUNT) return; int latencyUs = NetworkStackUtils.saturatedCast(durationUs); final ProbeEvent.Builder probeEventBuilder = ProbeEvent.newBuilder() .setLatencyMicros(latencyUs) .setProbeType(type) .setProbeResult(result); if (capportData != null) { final long secondsRemaining = (capportData.getExpiryTimeMillis() - currentTimeMillis()) / 1000; mCapportApiDataBuilder .setRemainingTtlSecs(NetworkStackUtils.saturatedCast(secondsRemaining)) // TODO: rename this field to setRemainingKBytes, or use a long .setRemainingBytes( NetworkStackUtils.saturatedCast(capportData.getByteLimit() / 1000)) .setHasPortalUrl((capportData.getUserPortalUrl() != null)) .setHasVenueInfo((capportData.getVenueInfoUrl() != null)); probeEventBuilder.setCapportApiData(mCapportApiDataBuilder); } mProbeEventsBuilder.addProbeEvent(probeEventBuilder); } /** * Write the network validation info to mStatsBuilder. */ public void setValidationResult(int result, String redirectUrl) { mStatsBuilder.setValidationResult(validationResultToEnum(result, redirectUrl)); } /** * Write the NetworkValidationReported proto to statsd. * * <p>This is a no-op if {@link #startCollection(NetworkCapabilities)} was not called since the * last call to this method. */ public NetworkValidationReported maybeStopCollectionAndSend() { if (!mWatch.isStarted()) return null; mStatsBuilder.setProbeEvents(mProbeEventsBuilder); mStatsBuilder.setLatencyMicros(NetworkStackUtils.saturatedCast(mWatch.stop())); mStatsBuilder.setValidationIndex(mValidationIndex); // write a random value(0 ~ 999) for sampling. mStatsBuilder.setRandomNumber((int) (Math.random() * 1000)); final NetworkValidationReported stats = mStatsBuilder.build(); final byte[] probeEvents = stats.getProbeEvents().toByteArray(); NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_VALIDATION_REPORTED, stats.getTransportType().getNumber(), probeEvents, stats.getValidationResult().getNumber(), stats.getLatencyMicros(), stats.getValidationIndex(), stats.getRandomNumber()); mWatch.reset(); return stats; } } Loading
AndroidManifest.xml +2 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" android:sharedUserId="android.uid.networkstack" android:versionCode="300900400" android:versionName="r_aml_300900400" android:versionCode="300900500" android:versionName="r_aml_300900500" > <!-- Permissions must be defined here, and not in the base manifest, as the network stack running in the system server process does not need any permission, and having privileged Loading
apishim/30/com/android/networkstack/apishim/api30/CaptivePortalDataShimImpl.java +10 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,16 @@ public class CaptivePortalDataShimImpl return mData.isCaptive(); } @Override public long getByteLimit() { return mData.getByteLimit(); } @Override public long getExpiryTimeMillis() { return mData.getExpiryTimeMillis(); } @Override public Uri getUserPortalUrl() { return mData.getUserPortalUrl(); Loading
apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java +10 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,16 @@ public interface CaptivePortalDataShim { */ boolean isCaptive(); /** * @see android.net.CaptivePortalData#getByteLimit() */ long getByteLimit(); /** * @see android.net.CaptivePortalData#getExpiryTimeMillis() */ long getExpiryTimeMillis(); /** * @see android.net.CaptivePortalData#getUserPortalUrl() */ Loading
src/android/net/util/NetworkStackUtils.java +15 −0 Original line number Diff line number Diff line Loading @@ -226,6 +226,15 @@ public class NetworkStackUtils { public static final String DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION = "dns_probe_private_ip_no_internet"; /** * Experiment flag to enable validation metrics sent by NetworkMonitor. * * Metrics are sent by default. They can be disabled by setting the flag to a number greater * than the APK version (for example 999999999). * @see #isFeatureEnabled(Context, String, String, boolean) */ public static final String VALIDATION_METRICS_VERSION = "validation_metrics_version"; static { System.loadLibrary("networkstackutilsjni"); } Loading Loading @@ -348,6 +357,9 @@ public class NetworkStackUtils { * {@link DeviceConfig} is enabled by comparing NetworkStack module version {@link NetworkStack} * with current version of property. If this property version is valid, the corresponding * experimental feature would be enabled, otherwise disabled. * * This is useful to ensure that if a module install is rolled back, flags are not left fully * rolled out on a version where they have not been well tested. * @param context The global context information about an app environment. * @param namespace The namespace containing the property to look up. * @param name The name of the property to look up. Loading @@ -363,6 +375,9 @@ public class NetworkStackUtils { * {@link DeviceConfig} is enabled by comparing NetworkStack module version {@link NetworkStack} * with current version of property. If this property version is valid, the corresponding * experimental feature would be enabled, otherwise disabled. * * This is useful to ensure that if a module install is rolled back, flags are not left fully * rolled out on a version where they have not been well tested. * @param context The global context information about an app environment. * @param namespace The namespace containing the property to look up. * @param name The name of the property to look up. Loading
src/com/android/networkstack/metrics/NetworkValidationMetrics.java 0 → 100644 +254 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 com.android.networkstack.metrics; import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; import static java.lang.System.currentTimeMillis; import android.net.INetworkMonitor; import android.net.NetworkCapabilities; import android.net.captiveportal.CaptivePortalProbeResult; import android.net.metrics.ValidationProbeEvent; import android.net.util.NetworkStackUtils; import android.net.util.Stopwatch; import android.stats.connectivity.ProbeResult; import android.stats.connectivity.ProbeType; import android.stats.connectivity.TransportType; import android.stats.connectivity.ValidationResult; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.networkstack.apishim.common.CaptivePortalDataShim; /** * Class to record the network validation into statsd. * 1. Fill in NetworkValidationReported proto. * 2. Write the NetworkValidationReported proto into statsd. * @hide */ public class NetworkValidationMetrics { private final NetworkValidationReported.Builder mStatsBuilder = NetworkValidationReported.newBuilder(); private final ProbeEvents.Builder mProbeEventsBuilder = ProbeEvents.newBuilder(); private final CapportApiData.Builder mCapportApiDataBuilder = CapportApiData.newBuilder(); private final Stopwatch mWatch = new Stopwatch(); private int mValidationIndex = 0; // Define a maximum size that can store events. public static final int MAX_PROBE_EVENTS_COUNT = 20; /** * Reset this NetworkValidationMetrics and start collecting timing and metrics. * * <p>This must be called when validation starts. */ public void startCollection(@Nullable NetworkCapabilities nc) { mStatsBuilder.clear(); mProbeEventsBuilder.clear(); mCapportApiDataBuilder.clear(); mWatch.restart(); mStatsBuilder.setTransportType(getTransportTypeFromNC(nc)); mValidationIndex++; } /** * Returns the enum TransportType. * * <p>This method only supports a limited set of common transport type combinations that can be * measured through metrics, and will return {@link TransportType#TT_UNKNOWN} for others. This * ensures that, for example, metrics for a TRANSPORT_NEW_UNKNOWN | TRANSPORT_ETHERNET network * cannot get aggregated with / compared with a "normal" TRANSPORT_ETHERNET network without * noticing. * * @param nc Capabilities to extract transport type from. * @return the TransportType which is defined in * core/proto/android/stats/connectivity/network_stack.proto */ @VisibleForTesting public static TransportType getTransportTypeFromNC(@Nullable NetworkCapabilities nc) { if (nc == null) return TransportType.TT_UNKNOWN; final int trCount = nc.getTransportTypes().length; boolean hasCellular = nc.hasTransport(TRANSPORT_CELLULAR); boolean hasWifi = nc.hasTransport(TRANSPORT_WIFI); boolean hasBT = nc.hasTransport(TRANSPORT_BLUETOOTH); boolean hasEthernet = nc.hasTransport(TRANSPORT_ETHERNET); boolean hasVpn = nc.hasTransport(TRANSPORT_VPN); boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE); boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN); // VPN networks are not subject to validation and should not see validation stats, but // metrics could be added to measure private DNS probes only. if (trCount == 3 && hasCellular && hasWifi && hasVpn) { return TransportType.TT_WIFI_CELLULAR_VPN; } if (trCount == 2 && hasVpn) { if (hasWifi) return TransportType.TT_WIFI_VPN; if (hasCellular) return TransportType.TT_CELLULAR_VPN; if (hasBT) return TransportType.TT_BLUETOOTH_VPN; if (hasEthernet) return TransportType.TT_ETHERNET_VPN; } if (trCount == 1) { if (hasWifi) return TransportType.TT_WIFI; if (hasCellular) return TransportType.TT_CELLULAR; if (hasBT) return TransportType.TT_BLUETOOTH; if (hasEthernet) return TransportType.TT_ETHERNET; if (hasWifiAware) return TransportType.TT_WIFI_AWARE; if (hasLopan) return TransportType.TT_LOWPAN; // TODO: consider having a TT_VPN for VPN-only transport } return TransportType.TT_UNKNOWN; } /** * Map {@link ValidationProbeEvent} to {@link ProbeType}. */ public static ProbeType probeTypeToEnum(final int probeType) { switch(probeType) { case ValidationProbeEvent.PROBE_DNS: return ProbeType.PT_DNS; case ValidationProbeEvent.PROBE_HTTP: return ProbeType.PT_HTTP; case ValidationProbeEvent.PROBE_HTTPS: return ProbeType.PT_HTTPS; case ValidationProbeEvent.PROBE_PAC: return ProbeType.PT_PAC; case ValidationProbeEvent.PROBE_FALLBACK: return ProbeType.PT_FALLBACK; case ValidationProbeEvent.PROBE_PRIVDNS: return ProbeType.PT_PRIVDNS; default: return ProbeType.PT_UNKNOWN; } } /** * Map {@link CaptivePortalProbeResult} to {@link ProbeResult}. */ public static ProbeResult httpProbeResultToEnum(final CaptivePortalProbeResult result) { if (result == null) return ProbeResult.PR_UNKNOWN; if (result.isSuccessful()) { return ProbeResult.PR_SUCCESS; } else if (result.isDnsPrivateIpResponse()) { return ProbeResult.PR_PRIVATE_IP_DNS; } else if (result.isFailed()) { return ProbeResult.PR_FAILURE; } else if (result.isPortal()) { return ProbeResult.PR_PORTAL; } else { return ProbeResult.PR_UNKNOWN; } } /** * Map validation result (as per INetworkMonitor) to {@link ValidationResult}. */ @VisibleForTesting public static ValidationResult validationResultToEnum(int result, String redirectUrl) { // TODO: consider adding a VR_PARTIAL_SUCCESS field to track cases where users accepted // partial connectivity if ((result & INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID) != 0) { return ValidationResult.VR_SUCCESS; } else if (redirectUrl != null) { return ValidationResult.VR_PORTAL; } else if ((result & INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL) != 0) { return ValidationResult.VR_PARTIAL; } else { return ValidationResult.VR_FAILURE; } } /** * Add a network probe event to the metrics builder. */ public void addProbeEvent(final ProbeType type, final long durationUs, final ProbeResult result, @Nullable final CaptivePortalDataShim capportData) { // When the number of ProbeEvents of mProbeEventsBuilder exceeds // MAX_PROBE_EVENTS_COUNT, stop adding ProbeEvent. // TODO: consider recording the total number of probes in a separate field to know how // many probes are skipped. if (mProbeEventsBuilder.getProbeEventCount() >= MAX_PROBE_EVENTS_COUNT) return; int latencyUs = NetworkStackUtils.saturatedCast(durationUs); final ProbeEvent.Builder probeEventBuilder = ProbeEvent.newBuilder() .setLatencyMicros(latencyUs) .setProbeType(type) .setProbeResult(result); if (capportData != null) { final long secondsRemaining = (capportData.getExpiryTimeMillis() - currentTimeMillis()) / 1000; mCapportApiDataBuilder .setRemainingTtlSecs(NetworkStackUtils.saturatedCast(secondsRemaining)) // TODO: rename this field to setRemainingKBytes, or use a long .setRemainingBytes( NetworkStackUtils.saturatedCast(capportData.getByteLimit() / 1000)) .setHasPortalUrl((capportData.getUserPortalUrl() != null)) .setHasVenueInfo((capportData.getVenueInfoUrl() != null)); probeEventBuilder.setCapportApiData(mCapportApiDataBuilder); } mProbeEventsBuilder.addProbeEvent(probeEventBuilder); } /** * Write the network validation info to mStatsBuilder. */ public void setValidationResult(int result, String redirectUrl) { mStatsBuilder.setValidationResult(validationResultToEnum(result, redirectUrl)); } /** * Write the NetworkValidationReported proto to statsd. * * <p>This is a no-op if {@link #startCollection(NetworkCapabilities)} was not called since the * last call to this method. */ public NetworkValidationReported maybeStopCollectionAndSend() { if (!mWatch.isStarted()) return null; mStatsBuilder.setProbeEvents(mProbeEventsBuilder); mStatsBuilder.setLatencyMicros(NetworkStackUtils.saturatedCast(mWatch.stop())); mStatsBuilder.setValidationIndex(mValidationIndex); // write a random value(0 ~ 999) for sampling. mStatsBuilder.setRandomNumber((int) (Math.random() * 1000)); final NetworkValidationReported stats = mStatsBuilder.build(); final byte[] probeEvents = stats.getProbeEvents().toByteArray(); NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_VALIDATION_REPORTED, stats.getTransportType().getNumber(), probeEvents, stats.getValidationResult().getNumber(), stats.getLatencyMicros(), stats.getValidationIndex(), stats.getRandomNumber()); mWatch.reset(); return stats; } }