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

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

Snap for 6657150 from bf45240a to rvc-release

Change-Id: I44acb557f9b07f858f3e24f2b79c6ae15f2d4a36
parents ae9d21ac bf45240a
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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();
+10 −0
Original line number Diff line number Diff line
@@ -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()
     */
+15 −0
Original line number Diff line number Diff line
@@ -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");
    }
@@ -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.
@@ -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.
+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;
    }
}
+132 −13

File changed.

Preview size limit exceeded, changes collapsed.

Loading