Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d32b8a95 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 6617726 from f618c1ee to sc-release

Change-Id: I277f56a97e465ccb5bea10e8c14a0e8c904e70eb
parents 653ad048 f618c1ee
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -92,6 +92,16 @@ public class CaptivePortalDataShimImpl
        return mData.isCaptive();
        return mData.isCaptive();
    }
    }


    @Override
    public long getByteLimit() {
        return mData.getByteLimit();
    }

    @Override
    public long getExpiryTimeMillis() {
        return mData.getExpiryTimeMillis();
    }

    @Override
    @Override
    public Uri getUserPortalUrl() {
    public Uri getUserPortalUrl() {
        return mData.getUserPortalUrl();
        return mData.getUserPortalUrl();
+10 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,16 @@ public interface CaptivePortalDataShim {
     */
     */
    boolean isCaptive();
    boolean isCaptive();


    /**
     * @see android.net.CaptivePortalData#getByteLimit()
     */
    long getByteLimit();

    /**
     * @see android.net.CaptivePortalData#getExpiryTimeMillis()
     */
    long getExpiryTimeMillis();

    /**
    /**
     * @see android.net.CaptivePortalData#getUserPortalUrl()
     * @see android.net.CaptivePortalData#getUserPortalUrl()
     */
     */
+4 −2
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ aidl_interface {
            apex_available: [
            apex_available: [
                "//apex_available:platform",
                "//apex_available:platform",
                "com.android.wifi",
                "com.android.wifi",
                "com.android.bluetooth.updatable",
            ],
            ],
            // this is part of updatable modules(NetworkStack) which targets 29(Q)
            // this is part of updatable modules(NetworkStack) which targets 29(Q)
            min_sdk_version: "29",
            min_sdk_version: "29",
@@ -92,6 +93,7 @@ aidl_interface {
                "//apex_available:platform",
                "//apex_available:platform",
                "com.android.bluetooth.updatable",
                "com.android.bluetooth.updatable",
                "com.android.wifi",
                "com.android.wifi",
                "com.android.tethering",
            ],
            ],
            // this is part of updatable modules(NetworkStack) which targets 29(Q)
            // this is part of updatable modules(NetworkStack) which targets 29(Q)
            min_sdk_version: "29",
            min_sdk_version: "29",
@@ -133,8 +135,8 @@ java_library {
        "src/android/net/shared/**/*.java",
        "src/android/net/shared/**/*.java",
    ],
    ],
    static_libs: [
    static_libs: [
        "ipmemorystore-aidl-interfaces-java",
        "ipmemorystore-aidl-interfaces-unstable-java",
        "networkstack-aidl-interfaces-java",
        "networkstack-aidl-interfaces-unstable-java",
    ],
    ],
    visibility: [
    visibility: [
        "//frameworks/base/packages/Tethering",
        "//frameworks/base/packages/Tethering",
+220 −0
Original line number Original line 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.
     */
    public void reset(@Nullable NetworkCapabilities nc) {
        mStatsBuilder.clear();
        mProbeEventsBuilder.clear();
        mCapportApiDataBuilder.clear();
        mWatch.restart();
        mStatsBuilder.setTransportType(getTransportTypeFromNC(nc));
        mValidationIndex++;
    }

    /**
     * Returns the enum TransportType
     *
     * @param NetworkCapabilities
     * @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;
        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);

        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;
        if (hasWifiAware) return TransportType.TT_WIFI_AWARE;
        if (hasLopan) return TransportType.TT_LOWPAN;
        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) {
        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;
        }
    }

    /**
     * Write each network probe event to mProbeEventsBuilder.
     */
    public void setProbeEvent(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.
        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))
                .setRemainingBytes(NetworkStackUtils.saturatedCast(capportData.getByteLimit()))
                .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.
     */
    public NetworkValidationReported sendValidationStats() {
        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();

        NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_VALIDATION_REPORTED,
                mStats.getTransportType().getNumber(),
                probeEvents,
                mStats.getValidationResult().getNumber(),
                mStats.getLatencyMicros(),
                mStats.getValidationIndex(),
                mStats.getRandomNumber());
        mWatch.reset();
        return mStats;
    }
}
+82 −12
Original line number Original line Diff line number Diff line
@@ -129,6 +129,8 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings;
import android.stats.connectivity.ProbeResult;
import android.stats.connectivity.ProbeType;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants;
import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityNr;
import android.telephony.CellInfo;
import android.telephony.CellInfo;
@@ -155,6 +157,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.VisibleForTesting;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.StateMachine;
@@ -168,6 +171,7 @@ import com.android.networkstack.apishim.common.ShimUtils;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.metrics.DataStallDetectionStats;
import com.android.networkstack.metrics.DataStallDetectionStats;
import com.android.networkstack.metrics.DataStallStatsUtils;
import com.android.networkstack.metrics.DataStallStatsUtils;
import com.android.networkstack.metrics.NetworkValidationMetrics;
import com.android.networkstack.netlink.TcpSocketTracker;
import com.android.networkstack.netlink.TcpSocketTracker;
import com.android.networkstack.util.DnsUtils;
import com.android.networkstack.util.DnsUtils;
import com.android.server.NetworkStackService.NetworkStackServiceManager;
import com.android.server.NetworkStackService.NetworkStackServiceManager;
@@ -502,6 +506,10 @@ public class NetworkMonitor extends StateMachine {


    private final boolean mPrivateIpNoInternetEnabled;
    private final boolean mPrivateIpNoInternetEnabled;


    @GuardedBy("mNetworkValidationMetrics")
    private final NetworkValidationMetrics mNetworkValidationMetrics =
            new NetworkValidationMetrics();

    private int getCallbackVersion(INetworkMonitorCallbacks cb) {
    private int getCallbackVersion(INetworkMonitorCallbacks cb) {
        int version;
        int version;
        try {
        try {
@@ -778,6 +786,31 @@ public class NetworkMonitor extends StateMachine {
        }
        }
    }
    }


    private void recordMetricsReset(@Nullable NetworkCapabilities nc) {
        synchronized (mNetworkValidationMetrics) {
            mNetworkValidationMetrics.reset(nc);
        }
    }

    private void recordMetricsProbeEvent(ProbeType type, long latencyMicros, ProbeResult result,
            CaptivePortalDataShim capportData) {
        synchronized (mNetworkValidationMetrics) {
            mNetworkValidationMetrics.setProbeEvent(type, latencyMicros, result, capportData);
        }
    }

    private void recordMetricsValidationResult(int result, String redirectUrl) {
        synchronized (mNetworkValidationMetrics) {
            mNetworkValidationMetrics.setValidationResult(result, redirectUrl);
        }
    }

    private void recordMetricsValidationStats() {
        synchronized (mNetworkValidationMetrics) {
            mNetworkValidationMetrics.sendValidationStats();
        }
    }

    // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
    // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
    // does not entail any real state (hence no enter() or exit() routines).
    // does not entail any real state (hence no enter() or exit() routines).
    private class DefaultState extends State {
    private class DefaultState extends State {
@@ -790,6 +823,7 @@ public class NetworkMonitor extends StateMachine {
                    transitionTo(mEvaluatingState);
                    transitionTo(mEvaluatingState);
                    return HANDLED;
                    return HANDLED;
                case CMD_NETWORK_DISCONNECTED:
                case CMD_NETWORK_DISCONNECTED:
                    recordMetricsValidationStats();
                    logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED);
                    logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED);
                    quit();
                    quit();
                    return HANDLED;
                    return HANDLED;
@@ -941,6 +975,7 @@ public class NetworkMonitor extends StateMachine {
            initSocketTrackingIfRequired();
            initSocketTrackingIfRequired();
            // start periodical polling.
            // start periodical polling.
            sendTcpPollingEvent();
            sendTcpPollingEvent();
            recordMetricsValidationStats();
        }
        }


        private void initSocketTrackingIfRequired() {
        private void initSocketTrackingIfRequired() {
@@ -960,6 +995,9 @@ public class NetworkMonitor extends StateMachine {
                    transitionTo(mValidatedState);
                    transitionTo(mValidatedState);
                    break;
                    break;
                case CMD_EVALUATE_PRIVATE_DNS:
                case CMD_EVALUATE_PRIVATE_DNS:
                    // TODO: this causes reevaluation of a single probe that is not counted in
                    // metrics. Add support for such reevaluation probes in metrics, and log them
                    // separately.
                    transitionTo(mEvaluatingPrivateDnsState);
                    transitionTo(mEvaluatingPrivateDnsState);
                    break;
                    break;
                case EVENT_DNS_NOTIFICATION:
                case EVENT_DNS_NOTIFICATION:
@@ -1288,6 +1326,7 @@ public class NetworkMonitor extends StateMachine {
            sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
            sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
                    CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
                    CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
            mValidations++;
            mValidations++;
            recordMetricsValidationStats();
        }
        }


        @Override
        @Override
@@ -1319,6 +1358,10 @@ public class NetworkMonitor extends StateMachine {
                                notifyPrivateDnsConfigResolved();
                                notifyPrivateDnsConfigResolved();
                            } else {
                            } else {
                                handlePrivateDnsEvaluationFailure();
                                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 */,
                                        ProbeResult.PR_FAILURE, null /* capportData */);
                                break;
                                break;
                            }
                            }
                        }
                        }
@@ -1428,6 +1471,8 @@ public class NetworkMonitor extends StateMachine {
                validationLog(PROBE_PRIVDNS, host,
                validationLog(PROBE_PRIVDNS, host,
                        String.format("%dus - Error: %s", time, uhe.getMessage()));
                        String.format("%dus - Error: %s", time, uhe.getMessage()));
            }
            }
            recordMetricsProbeEvent(ProbeType.PT_PRIVDNS, time, success ? ProbeResult.PR_SUCCESS :
                    ProbeResult.PR_FAILURE, null /* capportData */);
            logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE);
            logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE);
            return success;
            return success;
        }
        }
@@ -1438,6 +1483,8 @@ public class NetworkMonitor extends StateMachine {


        @Override
        @Override
        public void enter() {
        public void enter() {
            recordMetricsValidationStats();
            recordMetricsReset(mNetworkCapabilities);
            if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
            if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
                //Don't continue to blame UID forever.
                //Don't continue to blame UID forever.
                TrafficStats.clearThreadStatsUid();
                TrafficStats.clearThreadStatsUid();
@@ -1519,6 +1566,7 @@ public class NetworkMonitor extends StateMachine {
    private class WaitingForNextProbeState extends State {
    private class WaitingForNextProbeState extends State {
        @Override
        @Override
        public void enter() {
        public void enter() {
            recordMetricsValidationStats();
            scheduleNextProbe();
            scheduleNextProbe();
        }
        }


@@ -2269,6 +2317,8 @@ public class NetworkMonitor extends StateMachine {
        // network validation (the HTTPS probe, which would likely fail anyway) or the PAC probe.
        // network validation (the HTTPS probe, which would likely fail anyway) or the PAC probe.
        if (mPrivateIpNoInternetEnabled && probeType == ValidationProbeEvent.PROBE_HTTP
        if (mPrivateIpNoInternetEnabled && probeType == ValidationProbeEvent.PROBE_HTTP
                && (proxy == null) && hasPrivateIpAddress(resolvedAddr)) {
                && (proxy == null) && hasPrivateIpAddress(resolvedAddr)) {
            recordMetricsProbeEvent(NetworkValidationMetrics.probeTypeToEnum(probeType),
                    0 /* latency */, ProbeResult.PR_PRIVATE_IP_DNS, null /* capportData */);
            return CaptivePortalProbeResult.PRIVATE_IP;
            return CaptivePortalProbeResult.PRIVATE_IP;
        }
        }
        return sendHttpProbe(url, probeType, null);
        return sendHttpProbe(url, probeType, null);
@@ -2300,6 +2350,9 @@ public class NetworkMonitor extends StateMachine {
            result = ValidationProbeEvent.DNS_FAILURE;
            result = ValidationProbeEvent.DNS_FAILURE;
        }
        }
        final long latency = watch.stop();
        final long latency = watch.stop();
        recordMetricsProbeEvent(ProbeType.PT_DNS, latency,
                (result == ValidationProbeEvent.DNS_SUCCESS) ? ProbeResult.PR_SUCCESS :
                ProbeResult.PR_FAILURE, null /* capportData */);
        logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result);
        logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result);
        return addresses;
        return addresses;
    }
    }
@@ -2417,12 +2470,17 @@ public class NetworkMonitor extends StateMachine {
        }
        }
        logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
        logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);


        final CaptivePortalProbeResult probeResult;
        if (probeSpec == null) {
        if (probeSpec == null) {
            return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString(),
            probeResult = new CaptivePortalProbeResult(httpResponseCode, redirectUrl,
                    1 << probeType);
                    url.toString(),   1 << probeType);
        } else {
        } else {
            return probeSpec.getResult(httpResponseCode, redirectUrl);
            probeResult = probeSpec.getResult(httpResponseCode, redirectUrl);
        }
        }
        recordMetricsProbeEvent(NetworkValidationMetrics.probeTypeToEnum(probeType),
                probeTimer.stop(), NetworkValidationMetrics.httpProbeResultToEnum(probeResult),
                null /* capportData */);
        return probeResult;
    }
    }


    @VisibleForTesting
    @VisibleForTesting
@@ -2576,8 +2634,7 @@ public class NetworkMonitor extends StateMachine {
            super(properties, proxy, url, captivePortalApiUrl);
            super(properties, proxy, url, captivePortalApiUrl);
        }
        }


        private CaptivePortalDataShim tryCapportApiProbe() {
        private CaptivePortalDataShim sendCapportApiProbe() {
            if (mCaptivePortalApiUrl == null) return null;
            validationLog("Fetching captive portal data from " + mCaptivePortalApiUrl);
            validationLog("Fetching captive portal data from " + mCaptivePortalApiUrl);


            final String apiContent;
            final String apiContent;
@@ -2616,26 +2673,38 @@ public class NetworkMonitor extends StateMachine {


            try {
            try {
                final JSONObject info = new JSONObject(apiContent);
                final JSONObject info = new JSONObject(apiContent);
                return CaptivePortalDataShimImpl.fromJson(info);
                final CaptivePortalDataShim capportData = CaptivePortalDataShimImpl.fromJson(info);
                if (capportData != null && capportData.isCaptive()
                        && capportData.getUserPortalUrl() == null) {
                    validationLog("Missing user-portal-url from capport response");
                    return null;
                }
                return capportData;
            } catch (JSONException e) {
            } catch (JSONException e) {
                validationLog("Could not parse capport API JSON: " + e.getMessage());
                validationLog("Could not parse capport API JSON: " + e.getMessage());
                return null;
                return null;
            } catch (UnsupportedApiLevelException e) {
            } catch (UnsupportedApiLevelException e) {
                // This should never happen because LinkProperties would not have a capport URL
                // before R.
                validationLog("Platform API too low to support capport API");
                validationLog("Platform API too low to support capport API");
                return null;
                return null;
            }
            }
        }
        }


        private CaptivePortalDataShim tryCapportApiProbe() {
            if (mCaptivePortalApiUrl == null) return null;
            final Stopwatch capportApiWatch = new Stopwatch().start();
            final CaptivePortalDataShim capportData = sendCapportApiProbe();
            recordMetricsProbeEvent(ProbeType.PT_CAPPORT_API, capportApiWatch.stop(),
                    capportData == null ? ProbeResult.PR_FAILURE : ProbeResult.PR_SUCCESS,
                    capportData);
            return capportData;
        }

        @Override
        @Override
        protected CaptivePortalProbeResult sendProbe() {
        protected CaptivePortalProbeResult sendProbe() {
            final CaptivePortalDataShim capportData = tryCapportApiProbe();
            final CaptivePortalDataShim capportData = tryCapportApiProbe();
            if (capportData != null && capportData.isCaptive()) {
            if (capportData != null && capportData.isCaptive()) {
                if (capportData.getUserPortalUrl() == null) {
                    validationLog("Missing user-portal-url from capport response");
                    return new CapportApiProbeResult(
                            sendDnsAndHttpProbes(mProxy, mUrl, ValidationProbeEvent.PROBE_HTTP),
                            null /* capportData */);
                }
                final String loginUrlString = capportData.getUserPortalUrl().toString();
                final String loginUrlString = capportData.getUserPortalUrl().toString();
                // Starting from R (where CaptivePortalData was introduced), the captive portal app
                // Starting from R (where CaptivePortalData was introduced), the captive portal app
                // delegates to NetworkMonitor for verifying when the network validates instead of
                // delegates to NetworkMonitor for verifying when the network validates instead of
@@ -3357,6 +3426,7 @@ public class NetworkMonitor extends StateMachine {
            p.redirectUrl = redirectUrl;
            p.redirectUrl = redirectUrl;
            p.timestampMillis = SystemClock.elapsedRealtime();
            p.timestampMillis = SystemClock.elapsedRealtime();
            notifyNetworkTested(p);
            notifyNetworkTested(p);
            recordMetricsValidationResult(result, redirectUrl);
        }
        }


        @VisibleForTesting
        @VisibleForTesting
Loading