Loading src/android/net/util/NetworkStackUtils.java +15 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,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 @@ -350,6 +359,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 @@ -365,6 +377,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 +59 −25 Original line number Diff line number Diff line Loading @@ -60,9 +60,11 @@ public class NetworkValidationMetrics { public static final int MAX_PROBE_EVENTS_COUNT = 20; /** * Reset this NetworkValidationMetrics. * Reset this NetworkValidationMetrics and start collecting timing and metrics. * * <p>This must be called when validation starts. */ public void reset(@Nullable NetworkCapabilities nc) { public void startCollection(@Nullable NetworkCapabilities nc) { mStatsBuilder.clear(); mProbeEventsBuilder.clear(); mCapportApiDataBuilder.clear(); Loading @@ -72,16 +74,23 @@ public class NetworkValidationMetrics { } /** * Returns the enum TransportType * 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 NetworkCapabilities * @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) { 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); Loading @@ -90,13 +99,29 @@ public class NetworkValidationMetrics { boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE); boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN); if (hasCellular && hasWifi && hasVpn) return TransportType.TT_WIFI_CELLULAR_VPN; if (hasWifi) return hasVpn ? TransportType.TT_WIFI_VPN : TransportType.TT_WIFI; if (hasCellular) return hasVpn ? TransportType.TT_CELLULAR_VPN : TransportType.TT_CELLULAR; if (hasBT) return hasVpn ? TransportType.TT_BLUETOOTH_VPN : TransportType.TT_BLUETOOTH; if (hasEthernet) return hasVpn ? TransportType.TT_ETHERNET_VPN : TransportType.TT_ETHERNET; // 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; } Loading Loading @@ -146,6 +171,8 @@ public class NetworkValidationMetrics { */ @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) { Loading @@ -158,12 +185,14 @@ public class NetworkValidationMetrics { } /** * Write each network probe event to mProbeEventsBuilder. * Add a network probe event to the metrics builder. */ public void setProbeEvent(final ProbeType type, final long durationUs, final ProbeResult result, 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); Loading @@ -178,7 +207,9 @@ public class NetworkValidationMetrics { (capportData.getExpiryTimeMillis() - currentTimeMillis()) / 1000; mCapportApiDataBuilder .setRemainingTtlSecs(NetworkStackUtils.saturatedCast(secondsRemaining)) .setRemainingBytes(NetworkStackUtils.saturatedCast(capportData.getByteLimit())) // 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); Loading @@ -196,25 +227,28 @@ public class NetworkValidationMetrics { /** * 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 sendValidationStats() { 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 mStats = mStatsBuilder.build(); final byte[] probeEvents = mStats.getProbeEvents().toByteArray(); final NetworkValidationReported stats = mStatsBuilder.build(); final byte[] probeEvents = stats.getProbeEvents().toByteArray(); NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_VALIDATION_REPORTED, mStats.getTransportType().getNumber(), stats.getTransportType().getNumber(), probeEvents, mStats.getValidationResult().getNumber(), mStats.getLatencyMicros(), mStats.getValidationIndex(), mStats.getRandomNumber()); stats.getValidationResult().getNumber(), stats.getLatencyMicros(), stats.getValidationIndex(), stats.getRandomNumber()); mWatch.reset(); return mStats; return stats; } } src/com/android/server/connectivity/NetworkMonitor.java +75 −26 Original line number Diff line number Diff line Loading @@ -451,7 +451,14 @@ public class NetworkMonitor extends StateMachine { protected boolean mIsCaptivePortalCheckEnabled; private boolean mUseHttps; // The total number of captive portal detection attempts for this NetworkMonitor instance. /** * The total number of completed validation attempts (network validated or a captive portal was * detected) for this NetworkMonitor instance. * This does not include attempts that were interrupted, retried or finished with a result that * is not success or portal. See {@code mValidationIndex} in {@link NetworkValidationMetrics} * for a count of all attempts. * TODO: remove when removing legacy metrics. */ private int mValidations = 0; // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. Loading Loading @@ -506,6 +513,14 @@ public class NetworkMonitor extends StateMachine { private final boolean mPrivateIpNoInternetEnabled; private final boolean mMetricsEnabled; // The validation metrics are accessed by individual probe threads, and by the StateMachine // thread. All accesses must be synchronized to make sure the StateMachine thread can see // reports from all probes. // TODO: as that most usage is in the StateMachine thread and probes only add their probe // events, consider having probes return their stats to the StateMachine, and only access this // member on the StateMachine thread without synchronization. @GuardedBy("mNetworkValidationMetrics") private final NetworkValidationMetrics mNetworkValidationMetrics = new NetworkValidationMetrics(); Loading Loading @@ -574,6 +589,8 @@ public class NetworkMonitor extends StateMachine { mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(); mPrivateIpNoInternetEnabled = getIsPrivateIpNoInternetEnabled(); mMetricsEnabled = deps.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, NetworkStackUtils.VALIDATION_METRICS_VERSION, true /* defaultEnabled */); mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalHttpsUrls = makeCaptivePortalHttpsUrls(); Loading Loading @@ -786,28 +803,48 @@ public class NetworkMonitor extends StateMachine { } } private void recordMetricsReset(@Nullable NetworkCapabilities nc) { private void startMetricsCollection() { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.reset(nc); mNetworkValidationMetrics.startCollection(mNetworkCapabilities); } } catch (Exception e) { Log.wtf(TAG, "Error resetting validation metrics", e); } } private void recordMetricsProbeEvent(ProbeType type, long latencyMicros, ProbeResult result, private void recordProbeEventMetrics(ProbeType type, long latencyMicros, ProbeResult result, CaptivePortalDataShim capportData) { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.setProbeEvent(type, latencyMicros, result, capportData); mNetworkValidationMetrics.addProbeEvent(type, latencyMicros, result, capportData); } } catch (Exception e) { Log.wtf(TAG, "Error recording probe event", e); } } private void recordMetricsValidationResult(int result, String redirectUrl) { private void recordValidationResult(int result, String redirectUrl) { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.setValidationResult(result, redirectUrl); } } catch (Exception e) { Log.wtf(TAG, "Error recording validation result", e); } } private void recordMetricsValidationStats() { private void maybeStopCollectionAndSendMetrics() { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.sendValidationStats(); mNetworkValidationMetrics.maybeStopCollectionAndSend(); } } catch (Exception e) { Log.wtf(TAG, "Error sending validation stats", e); } } Loading @@ -823,7 +860,7 @@ public class NetworkMonitor extends StateMachine { transitionTo(mEvaluatingState); return HANDLED; case CMD_NETWORK_DISCONNECTED: recordMetricsValidationStats(); maybeStopCollectionAndSendMetrics(); logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED); quit(); return HANDLED; Loading Loading @@ -975,7 +1012,7 @@ public class NetworkMonitor extends StateMachine { initSocketTrackingIfRequired(); // start periodical polling. sendTcpPollingEvent(); recordMetricsValidationStats(); maybeStopCollectionAndSendMetrics(); } private void initSocketTrackingIfRequired() { Loading Loading @@ -1326,7 +1363,7 @@ public class NetworkMonitor extends StateMachine { sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */, CAPTIVE_PORTAL_REEVALUATE_DELAY_MS); mValidations++; recordMetricsValidationStats(); maybeStopCollectionAndSendMetrics(); } @Override Loading Loading @@ -1360,7 +1397,9 @@ public class NetworkMonitor extends StateMachine { handlePrivateDnsEvaluationFailure(); // The private DNS probe fails-fast if the server hostname cannot // be resolved. Record it as a failure with zero latency. recordMetricsProbeEvent(ProbeType.PT_PRIVDNS, 0 /* latency */, // TODO: refactor this together with the probe recorded in // sendPrivateDnsProbe, so logging is symmetric / easier to follow. recordProbeEventMetrics(ProbeType.PT_PRIVDNS, 0 /* latency */, ProbeResult.PR_FAILURE, null /* capportData */); break; } Loading Loading @@ -1471,7 +1510,7 @@ public class NetworkMonitor extends StateMachine { validationLog(PROBE_PRIVDNS, host, String.format("%dus - Error: %s", time, uhe.getMessage())); } recordMetricsProbeEvent(ProbeType.PT_PRIVDNS, time, success ? ProbeResult.PR_SUCCESS : recordProbeEventMetrics(ProbeType.PT_PRIVDNS, time, success ? ProbeResult.PR_SUCCESS : ProbeResult.PR_FAILURE, null /* capportData */); logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE); return success; Loading @@ -1483,8 +1522,14 @@ public class NetworkMonitor extends StateMachine { @Override public void enter() { recordMetricsValidationStats(); recordMetricsReset(mNetworkCapabilities); // When starting a full probe cycle here, record any pending stats (for example if // CMD_FORCE_REEVALUATE was called before evaluation finished, as can happen in // EvaluatingPrivateDnsState). maybeStopCollectionAndSendMetrics(); // Restart the metrics collection timers. Metrics will be stopped and sent when the // validation attempt finishes (as success, failure or portal), or if it is interrupted // (by being restarted or if NetworkMonitor stops). startMetricsCollection(); if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) { //Don't continue to blame UID forever. TrafficStats.clearThreadStatsUid(); Loading Loading @@ -1566,7 +1611,9 @@ public class NetworkMonitor extends StateMachine { private class WaitingForNextProbeState extends State { @Override public void enter() { recordMetricsValidationStats(); // Send metrics for this evaluation attempt. Metrics collection (and its timers) will be // restarted when the next probe starts. maybeStopCollectionAndSendMetrics(); scheduleNextProbe(); } Loading Loading @@ -2312,7 +2359,7 @@ public class NetworkMonitor extends StateMachine { // network validation (the HTTPS probe, which would likely fail anyway) or the PAC probe. if (mPrivateIpNoInternetEnabled && probeType == ValidationProbeEvent.PROBE_HTTP && (proxy == null) && hasPrivateIpAddress(resolvedAddr)) { recordMetricsProbeEvent(NetworkValidationMetrics.probeTypeToEnum(probeType), recordProbeEventMetrics(NetworkValidationMetrics.probeTypeToEnum(probeType), 0 /* latency */, ProbeResult.PR_PRIVATE_IP_DNS, null /* capportData */); return CaptivePortalProbeResult.PRIVATE_IP; } Loading Loading @@ -2345,7 +2392,7 @@ public class NetworkMonitor extends StateMachine { result = ValidationProbeEvent.DNS_FAILURE; } final long latency = watch.stop(); recordMetricsProbeEvent(ProbeType.PT_DNS, latency, recordProbeEventMetrics(ProbeType.PT_DNS, latency, (result == ValidationProbeEvent.DNS_SUCCESS) ? ProbeResult.PR_SUCCESS : ProbeResult.PR_FAILURE, null /* capportData */); logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result); Loading Loading @@ -2472,7 +2519,7 @@ public class NetworkMonitor extends StateMachine { } else { probeResult = probeSpec.getResult(httpResponseCode, redirectUrl); } recordMetricsProbeEvent(NetworkValidationMetrics.probeTypeToEnum(probeType), recordProbeEventMetrics(NetworkValidationMetrics.probeTypeToEnum(probeType), probeTimer.stop(), NetworkValidationMetrics.httpProbeResultToEnum(probeResult), null /* capportData */); return probeResult; Loading Loading @@ -2630,6 +2677,8 @@ public class NetworkMonitor extends StateMachine { } private CaptivePortalDataShim sendCapportApiProbe() { // TODO: consider adding metrics counters for each case returning null in this method // (cases where the API is not implemented properly). validationLog("Fetching captive portal data from " + mCaptivePortalApiUrl); final String apiContent; Loading Loading @@ -2690,7 +2739,7 @@ public class NetworkMonitor extends StateMachine { if (mCaptivePortalApiUrl == null) return null; final Stopwatch capportApiWatch = new Stopwatch().start(); final CaptivePortalDataShim capportData = sendCapportApiProbe(); recordMetricsProbeEvent(ProbeType.PT_CAPPORT_API, capportApiWatch.stop(), recordProbeEventMetrics(ProbeType.PT_CAPPORT_API, capportApiWatch.stop(), capportData == null ? ProbeResult.PR_FAILURE : ProbeResult.PR_SUCCESS, capportData); return capportData; Loading Loading @@ -3421,7 +3470,7 @@ public class NetworkMonitor extends StateMachine { p.redirectUrl = redirectUrl; p.timestampMillis = SystemClock.elapsedRealtime(); notifyNetworkTested(p); recordMetricsValidationResult(result, redirectUrl); recordValidationResult(result, redirectUrl); } @VisibleForTesting Loading tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java +20 −22 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import android.net.INetworkMonitor; import android.net.NetworkCapabilities; Loading Loading @@ -54,7 +53,7 @@ public class NetworkValidationMetricsTest { private static final String TEST_VENUE_INFO_URL = "https://venue.example.com/info"; private static final int TTL_TOLERANCE_SECS = 10; private static final NetworkCapabilities WIFI_NOT_METERED_CAPABILITIES = private static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI); Loading Loading @@ -136,17 +135,17 @@ public class NetworkValidationMetricsTest { @Test public void testNetworkValidationMetrics_VerifyConsecutiveProbeFailure() throws Exception { final NetworkValidationMetrics Metrics = new NetworkValidationMetrics(); Metrics.reset(WIFI_NOT_METERED_CAPABILITIES); final NetworkValidationMetrics metrics = new NetworkValidationMetrics(); metrics.startCollection(WIFI_CAPABILITIES); // 1. PT_DNS probe Metrics.setProbeEvent(ProbeType.PT_DNS, 1234, ProbeResult.PR_SUCCESS, null); metrics.addProbeEvent(ProbeType.PT_DNS, 1234, ProbeResult.PR_SUCCESS, null); // 2. Consecutive PT_HTTP probe failure for (int i = 0; i < 30; i++) { Metrics.setProbeEvent(ProbeType.PT_HTTP, 1234, ProbeResult.PR_FAILURE, null); metrics.addProbeEvent(ProbeType.PT_HTTP, 1234, ProbeResult.PR_FAILURE, null); } // Write metric into statsd final NetworkValidationReported stats = Metrics.sendValidationStats(); final NetworkValidationReported stats = metrics.maybeStopCollectionAndSend(); // The maximum number of probe records should be the same as MAX_PROBE_EVENTS_COUNT final ProbeEvents probeEvents = stats.getProbeEvents(); Loading @@ -156,40 +155,39 @@ public class NetworkValidationMetricsTest { @Test public void testNetworkValidationMetrics_VerifyCollectMetrics() throws Exception { assumeTrue(CaptivePortalDataShimImpl.isSupported()); final long bytesRemaining = 10L; final long bytesRemaining = 12_345L; final long secondsRemaining = 3000L; String apiContent = "{'captive': true," + "'user-portal-url': '" + TEST_LOGIN_URL + "'," + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'," + "'bytes-remaining': " + bytesRemaining + "," + "'seconds-remaining': " + secondsRemaining + "}"; final NetworkValidationMetrics Metrics = new NetworkValidationMetrics(); final NetworkValidationMetrics metrics = new NetworkValidationMetrics(); final int validationIndex = 1; final long longlatency = 2147483649L; Metrics.reset(WIFI_NOT_METERED_CAPABILITIES); final long longlatency = Integer.MAX_VALUE + 12344567L; metrics.startCollection(WIFI_CAPABILITIES); final JSONObject info = new JSONObject(apiContent); final CaptivePortalDataShim captivePortalData = CaptivePortalDataShimImpl.fromJson(info); final CaptivePortalDataShim captivePortalData = CaptivePortalDataShimImpl.isSupported() ? CaptivePortalDataShimImpl.fromJson(info) : null; // 1. PT_CAPPORT_API probe w CapportApiData info Metrics.setProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_SUCCESS, metrics.addProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_SUCCESS, captivePortalData); // 2. PT_CAPPORT_API probe w/o CapportApiData info Metrics.setProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_FAILURE, null); metrics.addProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_FAILURE, null); // 3. PT_DNS probe Metrics.setProbeEvent(ProbeType.PT_DNS, 5678, ProbeResult.PR_FAILURE, null); metrics.addProbeEvent(ProbeType.PT_DNS, 5678, ProbeResult.PR_FAILURE, null); // 4. PT_HTTP probe Metrics.setProbeEvent(ProbeType.PT_HTTP, longlatency, ProbeResult.PR_PORTAL, null); metrics.addProbeEvent(ProbeType.PT_HTTP, longlatency, ProbeResult.PR_PORTAL, null); // add Validation result Metrics.setValidationResult(INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL, null); metrics.setValidationResult(INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL, null); // Write metric into statsd final NetworkValidationReported stats = Metrics.sendValidationStats(); final NetworkValidationReported stats = metrics.maybeStopCollectionAndSend(); // Verify: TransportType: WIFI assertEquals(TransportType.TT_WIFI, stats.getTransportType()); Loading @@ -207,13 +205,13 @@ public class NetworkValidationMetricsTest { assertEquals(ProbeType.PT_CAPPORT_API, probeEvent.getProbeType()); assertEquals(1234, probeEvent.getLatencyMicros()); assertEquals(ProbeResult.PR_SUCCESS, probeEvent.getProbeResult()); assertEquals(true, probeEvent.hasCapportApiData()); if (CaptivePortalDataShimImpl.isSupported()) { assertTrue(probeEvent.hasCapportApiData()); // Set secondsRemaining to 3000 and check that getRemainingTtlSecs is within 10 seconds final CapportApiData capportData = probeEvent.getCapportApiData(); assertTrue(capportData.getRemainingTtlSecs() <= secondsRemaining); assertTrue(capportData.getRemainingTtlSecs() + TTL_TOLERANCE_SECS > secondsRemaining); assertEquals(captivePortalData.getByteLimit(), capportData.getRemainingBytes()); assertEquals(captivePortalData.getByteLimit() / 1000, capportData.getRemainingBytes()); } else { assertFalse(probeEvent.hasCapportApiData()); } Loading Loading
src/android/net/util/NetworkStackUtils.java +15 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,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 @@ -350,6 +359,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 @@ -365,6 +377,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 +59 −25 Original line number Diff line number Diff line Loading @@ -60,9 +60,11 @@ public class NetworkValidationMetrics { public static final int MAX_PROBE_EVENTS_COUNT = 20; /** * Reset this NetworkValidationMetrics. * Reset this NetworkValidationMetrics and start collecting timing and metrics. * * <p>This must be called when validation starts. */ public void reset(@Nullable NetworkCapabilities nc) { public void startCollection(@Nullable NetworkCapabilities nc) { mStatsBuilder.clear(); mProbeEventsBuilder.clear(); mCapportApiDataBuilder.clear(); Loading @@ -72,16 +74,23 @@ public class NetworkValidationMetrics { } /** * Returns the enum TransportType * 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 NetworkCapabilities * @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) { 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); Loading @@ -90,13 +99,29 @@ public class NetworkValidationMetrics { boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE); boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN); if (hasCellular && hasWifi && hasVpn) return TransportType.TT_WIFI_CELLULAR_VPN; if (hasWifi) return hasVpn ? TransportType.TT_WIFI_VPN : TransportType.TT_WIFI; if (hasCellular) return hasVpn ? TransportType.TT_CELLULAR_VPN : TransportType.TT_CELLULAR; if (hasBT) return hasVpn ? TransportType.TT_BLUETOOTH_VPN : TransportType.TT_BLUETOOTH; if (hasEthernet) return hasVpn ? TransportType.TT_ETHERNET_VPN : TransportType.TT_ETHERNET; // 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; } Loading Loading @@ -146,6 +171,8 @@ public class NetworkValidationMetrics { */ @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) { Loading @@ -158,12 +185,14 @@ public class NetworkValidationMetrics { } /** * Write each network probe event to mProbeEventsBuilder. * Add a network probe event to the metrics builder. */ public void setProbeEvent(final ProbeType type, final long durationUs, final ProbeResult result, 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); Loading @@ -178,7 +207,9 @@ public class NetworkValidationMetrics { (capportData.getExpiryTimeMillis() - currentTimeMillis()) / 1000; mCapportApiDataBuilder .setRemainingTtlSecs(NetworkStackUtils.saturatedCast(secondsRemaining)) .setRemainingBytes(NetworkStackUtils.saturatedCast(capportData.getByteLimit())) // 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); Loading @@ -196,25 +227,28 @@ public class NetworkValidationMetrics { /** * 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 sendValidationStats() { 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 mStats = mStatsBuilder.build(); final byte[] probeEvents = mStats.getProbeEvents().toByteArray(); final NetworkValidationReported stats = mStatsBuilder.build(); final byte[] probeEvents = stats.getProbeEvents().toByteArray(); NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_VALIDATION_REPORTED, mStats.getTransportType().getNumber(), stats.getTransportType().getNumber(), probeEvents, mStats.getValidationResult().getNumber(), mStats.getLatencyMicros(), mStats.getValidationIndex(), mStats.getRandomNumber()); stats.getValidationResult().getNumber(), stats.getLatencyMicros(), stats.getValidationIndex(), stats.getRandomNumber()); mWatch.reset(); return mStats; return stats; } }
src/com/android/server/connectivity/NetworkMonitor.java +75 −26 Original line number Diff line number Diff line Loading @@ -451,7 +451,14 @@ public class NetworkMonitor extends StateMachine { protected boolean mIsCaptivePortalCheckEnabled; private boolean mUseHttps; // The total number of captive portal detection attempts for this NetworkMonitor instance. /** * The total number of completed validation attempts (network validated or a captive portal was * detected) for this NetworkMonitor instance. * This does not include attempts that were interrupted, retried or finished with a result that * is not success or portal. See {@code mValidationIndex} in {@link NetworkValidationMetrics} * for a count of all attempts. * TODO: remove when removing legacy metrics. */ private int mValidations = 0; // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. Loading Loading @@ -506,6 +513,14 @@ public class NetworkMonitor extends StateMachine { private final boolean mPrivateIpNoInternetEnabled; private final boolean mMetricsEnabled; // The validation metrics are accessed by individual probe threads, and by the StateMachine // thread. All accesses must be synchronized to make sure the StateMachine thread can see // reports from all probes. // TODO: as that most usage is in the StateMachine thread and probes only add their probe // events, consider having probes return their stats to the StateMachine, and only access this // member on the StateMachine thread without synchronization. @GuardedBy("mNetworkValidationMetrics") private final NetworkValidationMetrics mNetworkValidationMetrics = new NetworkValidationMetrics(); Loading Loading @@ -574,6 +589,8 @@ public class NetworkMonitor extends StateMachine { mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(); mPrivateIpNoInternetEnabled = getIsPrivateIpNoInternetEnabled(); mMetricsEnabled = deps.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, NetworkStackUtils.VALIDATION_METRICS_VERSION, true /* defaultEnabled */); mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalHttpsUrls = makeCaptivePortalHttpsUrls(); Loading Loading @@ -786,28 +803,48 @@ public class NetworkMonitor extends StateMachine { } } private void recordMetricsReset(@Nullable NetworkCapabilities nc) { private void startMetricsCollection() { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.reset(nc); mNetworkValidationMetrics.startCollection(mNetworkCapabilities); } } catch (Exception e) { Log.wtf(TAG, "Error resetting validation metrics", e); } } private void recordMetricsProbeEvent(ProbeType type, long latencyMicros, ProbeResult result, private void recordProbeEventMetrics(ProbeType type, long latencyMicros, ProbeResult result, CaptivePortalDataShim capportData) { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.setProbeEvent(type, latencyMicros, result, capportData); mNetworkValidationMetrics.addProbeEvent(type, latencyMicros, result, capportData); } } catch (Exception e) { Log.wtf(TAG, "Error recording probe event", e); } } private void recordMetricsValidationResult(int result, String redirectUrl) { private void recordValidationResult(int result, String redirectUrl) { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.setValidationResult(result, redirectUrl); } } catch (Exception e) { Log.wtf(TAG, "Error recording validation result", e); } } private void recordMetricsValidationStats() { private void maybeStopCollectionAndSendMetrics() { if (!mMetricsEnabled) return; try { synchronized (mNetworkValidationMetrics) { mNetworkValidationMetrics.sendValidationStats(); mNetworkValidationMetrics.maybeStopCollectionAndSend(); } } catch (Exception e) { Log.wtf(TAG, "Error sending validation stats", e); } } Loading @@ -823,7 +860,7 @@ public class NetworkMonitor extends StateMachine { transitionTo(mEvaluatingState); return HANDLED; case CMD_NETWORK_DISCONNECTED: recordMetricsValidationStats(); maybeStopCollectionAndSendMetrics(); logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED); quit(); return HANDLED; Loading Loading @@ -975,7 +1012,7 @@ public class NetworkMonitor extends StateMachine { initSocketTrackingIfRequired(); // start periodical polling. sendTcpPollingEvent(); recordMetricsValidationStats(); maybeStopCollectionAndSendMetrics(); } private void initSocketTrackingIfRequired() { Loading Loading @@ -1326,7 +1363,7 @@ public class NetworkMonitor extends StateMachine { sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */, CAPTIVE_PORTAL_REEVALUATE_DELAY_MS); mValidations++; recordMetricsValidationStats(); maybeStopCollectionAndSendMetrics(); } @Override Loading Loading @@ -1360,7 +1397,9 @@ public class NetworkMonitor extends StateMachine { handlePrivateDnsEvaluationFailure(); // The private DNS probe fails-fast if the server hostname cannot // be resolved. Record it as a failure with zero latency. recordMetricsProbeEvent(ProbeType.PT_PRIVDNS, 0 /* latency */, // TODO: refactor this together with the probe recorded in // sendPrivateDnsProbe, so logging is symmetric / easier to follow. recordProbeEventMetrics(ProbeType.PT_PRIVDNS, 0 /* latency */, ProbeResult.PR_FAILURE, null /* capportData */); break; } Loading Loading @@ -1471,7 +1510,7 @@ public class NetworkMonitor extends StateMachine { validationLog(PROBE_PRIVDNS, host, String.format("%dus - Error: %s", time, uhe.getMessage())); } recordMetricsProbeEvent(ProbeType.PT_PRIVDNS, time, success ? ProbeResult.PR_SUCCESS : recordProbeEventMetrics(ProbeType.PT_PRIVDNS, time, success ? ProbeResult.PR_SUCCESS : ProbeResult.PR_FAILURE, null /* capportData */); logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE); return success; Loading @@ -1483,8 +1522,14 @@ public class NetworkMonitor extends StateMachine { @Override public void enter() { recordMetricsValidationStats(); recordMetricsReset(mNetworkCapabilities); // When starting a full probe cycle here, record any pending stats (for example if // CMD_FORCE_REEVALUATE was called before evaluation finished, as can happen in // EvaluatingPrivateDnsState). maybeStopCollectionAndSendMetrics(); // Restart the metrics collection timers. Metrics will be stopped and sent when the // validation attempt finishes (as success, failure or portal), or if it is interrupted // (by being restarted or if NetworkMonitor stops). startMetricsCollection(); if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) { //Don't continue to blame UID forever. TrafficStats.clearThreadStatsUid(); Loading Loading @@ -1566,7 +1611,9 @@ public class NetworkMonitor extends StateMachine { private class WaitingForNextProbeState extends State { @Override public void enter() { recordMetricsValidationStats(); // Send metrics for this evaluation attempt. Metrics collection (and its timers) will be // restarted when the next probe starts. maybeStopCollectionAndSendMetrics(); scheduleNextProbe(); } Loading Loading @@ -2312,7 +2359,7 @@ public class NetworkMonitor extends StateMachine { // network validation (the HTTPS probe, which would likely fail anyway) or the PAC probe. if (mPrivateIpNoInternetEnabled && probeType == ValidationProbeEvent.PROBE_HTTP && (proxy == null) && hasPrivateIpAddress(resolvedAddr)) { recordMetricsProbeEvent(NetworkValidationMetrics.probeTypeToEnum(probeType), recordProbeEventMetrics(NetworkValidationMetrics.probeTypeToEnum(probeType), 0 /* latency */, ProbeResult.PR_PRIVATE_IP_DNS, null /* capportData */); return CaptivePortalProbeResult.PRIVATE_IP; } Loading Loading @@ -2345,7 +2392,7 @@ public class NetworkMonitor extends StateMachine { result = ValidationProbeEvent.DNS_FAILURE; } final long latency = watch.stop(); recordMetricsProbeEvent(ProbeType.PT_DNS, latency, recordProbeEventMetrics(ProbeType.PT_DNS, latency, (result == ValidationProbeEvent.DNS_SUCCESS) ? ProbeResult.PR_SUCCESS : ProbeResult.PR_FAILURE, null /* capportData */); logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result); Loading Loading @@ -2472,7 +2519,7 @@ public class NetworkMonitor extends StateMachine { } else { probeResult = probeSpec.getResult(httpResponseCode, redirectUrl); } recordMetricsProbeEvent(NetworkValidationMetrics.probeTypeToEnum(probeType), recordProbeEventMetrics(NetworkValidationMetrics.probeTypeToEnum(probeType), probeTimer.stop(), NetworkValidationMetrics.httpProbeResultToEnum(probeResult), null /* capportData */); return probeResult; Loading Loading @@ -2630,6 +2677,8 @@ public class NetworkMonitor extends StateMachine { } private CaptivePortalDataShim sendCapportApiProbe() { // TODO: consider adding metrics counters for each case returning null in this method // (cases where the API is not implemented properly). validationLog("Fetching captive portal data from " + mCaptivePortalApiUrl); final String apiContent; Loading Loading @@ -2690,7 +2739,7 @@ public class NetworkMonitor extends StateMachine { if (mCaptivePortalApiUrl == null) return null; final Stopwatch capportApiWatch = new Stopwatch().start(); final CaptivePortalDataShim capportData = sendCapportApiProbe(); recordMetricsProbeEvent(ProbeType.PT_CAPPORT_API, capportApiWatch.stop(), recordProbeEventMetrics(ProbeType.PT_CAPPORT_API, capportApiWatch.stop(), capportData == null ? ProbeResult.PR_FAILURE : ProbeResult.PR_SUCCESS, capportData); return capportData; Loading Loading @@ -3421,7 +3470,7 @@ public class NetworkMonitor extends StateMachine { p.redirectUrl = redirectUrl; p.timestampMillis = SystemClock.elapsedRealtime(); notifyNetworkTested(p); recordMetricsValidationResult(result, redirectUrl); recordValidationResult(result, redirectUrl); } @VisibleForTesting Loading
tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java +20 −22 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import android.net.INetworkMonitor; import android.net.NetworkCapabilities; Loading Loading @@ -54,7 +53,7 @@ public class NetworkValidationMetricsTest { private static final String TEST_VENUE_INFO_URL = "https://venue.example.com/info"; private static final int TTL_TOLERANCE_SECS = 10; private static final NetworkCapabilities WIFI_NOT_METERED_CAPABILITIES = private static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI); Loading Loading @@ -136,17 +135,17 @@ public class NetworkValidationMetricsTest { @Test public void testNetworkValidationMetrics_VerifyConsecutiveProbeFailure() throws Exception { final NetworkValidationMetrics Metrics = new NetworkValidationMetrics(); Metrics.reset(WIFI_NOT_METERED_CAPABILITIES); final NetworkValidationMetrics metrics = new NetworkValidationMetrics(); metrics.startCollection(WIFI_CAPABILITIES); // 1. PT_DNS probe Metrics.setProbeEvent(ProbeType.PT_DNS, 1234, ProbeResult.PR_SUCCESS, null); metrics.addProbeEvent(ProbeType.PT_DNS, 1234, ProbeResult.PR_SUCCESS, null); // 2. Consecutive PT_HTTP probe failure for (int i = 0; i < 30; i++) { Metrics.setProbeEvent(ProbeType.PT_HTTP, 1234, ProbeResult.PR_FAILURE, null); metrics.addProbeEvent(ProbeType.PT_HTTP, 1234, ProbeResult.PR_FAILURE, null); } // Write metric into statsd final NetworkValidationReported stats = Metrics.sendValidationStats(); final NetworkValidationReported stats = metrics.maybeStopCollectionAndSend(); // The maximum number of probe records should be the same as MAX_PROBE_EVENTS_COUNT final ProbeEvents probeEvents = stats.getProbeEvents(); Loading @@ -156,40 +155,39 @@ public class NetworkValidationMetricsTest { @Test public void testNetworkValidationMetrics_VerifyCollectMetrics() throws Exception { assumeTrue(CaptivePortalDataShimImpl.isSupported()); final long bytesRemaining = 10L; final long bytesRemaining = 12_345L; final long secondsRemaining = 3000L; String apiContent = "{'captive': true," + "'user-portal-url': '" + TEST_LOGIN_URL + "'," + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'," + "'bytes-remaining': " + bytesRemaining + "," + "'seconds-remaining': " + secondsRemaining + "}"; final NetworkValidationMetrics Metrics = new NetworkValidationMetrics(); final NetworkValidationMetrics metrics = new NetworkValidationMetrics(); final int validationIndex = 1; final long longlatency = 2147483649L; Metrics.reset(WIFI_NOT_METERED_CAPABILITIES); final long longlatency = Integer.MAX_VALUE + 12344567L; metrics.startCollection(WIFI_CAPABILITIES); final JSONObject info = new JSONObject(apiContent); final CaptivePortalDataShim captivePortalData = CaptivePortalDataShimImpl.fromJson(info); final CaptivePortalDataShim captivePortalData = CaptivePortalDataShimImpl.isSupported() ? CaptivePortalDataShimImpl.fromJson(info) : null; // 1. PT_CAPPORT_API probe w CapportApiData info Metrics.setProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_SUCCESS, metrics.addProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_SUCCESS, captivePortalData); // 2. PT_CAPPORT_API probe w/o CapportApiData info Metrics.setProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_FAILURE, null); metrics.addProbeEvent(ProbeType.PT_CAPPORT_API, 1234, ProbeResult.PR_FAILURE, null); // 3. PT_DNS probe Metrics.setProbeEvent(ProbeType.PT_DNS, 5678, ProbeResult.PR_FAILURE, null); metrics.addProbeEvent(ProbeType.PT_DNS, 5678, ProbeResult.PR_FAILURE, null); // 4. PT_HTTP probe Metrics.setProbeEvent(ProbeType.PT_HTTP, longlatency, ProbeResult.PR_PORTAL, null); metrics.addProbeEvent(ProbeType.PT_HTTP, longlatency, ProbeResult.PR_PORTAL, null); // add Validation result Metrics.setValidationResult(INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL, null); metrics.setValidationResult(INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL, null); // Write metric into statsd final NetworkValidationReported stats = Metrics.sendValidationStats(); final NetworkValidationReported stats = metrics.maybeStopCollectionAndSend(); // Verify: TransportType: WIFI assertEquals(TransportType.TT_WIFI, stats.getTransportType()); Loading @@ -207,13 +205,13 @@ public class NetworkValidationMetricsTest { assertEquals(ProbeType.PT_CAPPORT_API, probeEvent.getProbeType()); assertEquals(1234, probeEvent.getLatencyMicros()); assertEquals(ProbeResult.PR_SUCCESS, probeEvent.getProbeResult()); assertEquals(true, probeEvent.hasCapportApiData()); if (CaptivePortalDataShimImpl.isSupported()) { assertTrue(probeEvent.hasCapportApiData()); // Set secondsRemaining to 3000 and check that getRemainingTtlSecs is within 10 seconds final CapportApiData capportData = probeEvent.getCapportApiData(); assertTrue(capportData.getRemainingTtlSecs() <= secondsRemaining); assertTrue(capportData.getRemainingTtlSecs() + TTL_TOLERANCE_SECS > secondsRemaining); assertEquals(captivePortalData.getByteLimit(), capportData.getRemainingBytes()); assertEquals(captivePortalData.getByteLimit() / 1000, capportData.getRemainingBytes()); } else { assertFalse(probeEvent.hasCapportApiData()); } Loading