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

Commit b6bacdbe authored by lifr's avatar lifr Committed by Automerger Merge Worker
Browse files

Injecting network ip provision stats into statsd am: 342cb2c7 am: 42241f1d...

Injecting network ip provision stats into statsd am: 342cb2c7 am: 42241f1d am: be52cccb am: f4905bda am: 8adf5f79

Original change: https://android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/1313574

Change-Id: Ie98c3c2b98eac120ca953845e99034af66b9cb44
parents d86f8ac9 8adf5f79
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.stats.connectivity.DhcpFeature;
import android.system.ErrnoException;
import android.system.Os;
import android.util.EventLog;
@@ -101,6 +102,7 @@ import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
import com.android.networkstack.apishim.SocketUtilsShimImpl;
import com.android.networkstack.apishim.common.ShimUtils;
import com.android.networkstack.arp.ArpPacket;
import com.android.networkstack.metrics.IpProvisioningMetrics;

import java.io.FileDescriptor;
import java.io.IOException;
@@ -305,6 +307,8 @@ public class DhcpClient extends StateMachine {
    private final Context mContext;
    private final Random mRandom;
    private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
    @NonNull
    private final IpProvisioningMetrics mMetrics;

    // We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
    // be off-link as well as on-link).
@@ -378,9 +382,11 @@ public class DhcpClient extends StateMachine {
     */
    public static class Dependencies {
        private final NetworkStackIpMemoryStore mNetworkStackIpMemoryStore;
        private final IpProvisioningMetrics mMetrics;

        public Dependencies(NetworkStackIpMemoryStore store) {
        public Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics) {
            mNetworkStackIpMemoryStore = store;
            mMetrics = metrics;
        }

        /**
@@ -406,6 +412,13 @@ public class DhcpClient extends StateMachine {
            return mNetworkStackIpMemoryStore;
        }

        /**
         * Get a IpProvisioningMetrics instance.
         */
        public IpProvisioningMetrics getIpProvisioningMetrics() {
            return mMetrics;
        }

        /**
         * Return whether a feature guarded by a feature flag is enabled.
         * @see NetworkStackUtils#isFeatureEnabled(Context, String, String)
@@ -444,6 +457,7 @@ public class DhcpClient extends StateMachine {
        mController = controller;
        mIfaceName = iface;
        mIpMemoryStore = deps.getIpMemoryStore();
        mMetrics = deps.getIpProvisioningMetrics();

        // CHECKSTYLE:OFF IndentationCheck
        addState(mStoppedState);
@@ -484,6 +498,7 @@ public class DhcpClient extends StateMachine {
        final boolean sendHostname = deps.getSendHostnameOption(context);
        mHostname = sendHostname ? new HostnameTransliterator().transliterate(
                deps.getDeviceName(mContext)) : null;
        mMetrics.setHostnameTransinfo(sendHostname, mHostname != null);
    }

    public void registerForPreDhcpNotification() {
@@ -529,6 +544,15 @@ public class DhcpClient extends StateMachine {
                false /* defaultEnabled */);
    }

    private void recordMetricEnabledFeatures() {
        if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT);
        if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT);
        if (isDhcpIpConflictDetectEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_DAD);
        if (mConfiguration.isPreconnectionEnabled) {
            mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_FILS);
        }
    }

    private void confirmDhcpLease(DhcpPacket packet, DhcpResults results) {
        setDhcpLeaseExpiry(packet);
        acceptDhcpResults(results, "Confirmed");
@@ -610,6 +634,7 @@ public class DhcpClient extends StateMachine {
                    EventLog.writeEvent(snetTagId, bugId, uid, data);
                }
                mMetricsLog.log(mIfaceName, new DhcpErrorEvent(e.errorCode));
                mMetrics.addDhcpErrorCode(e.errorCode);
            }
        }

@@ -687,6 +712,7 @@ public class DhcpClient extends StateMachine {
        final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
                DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
                DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname);
        mMetrics.incrementCountForDiscover();
        return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
    }

@@ -705,6 +731,7 @@ public class DhcpClient extends StateMachine {
        String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
                             " request=" + requestedAddress.getHostAddress() +
                             " serverid=" + serverStr;
        mMetrics.incrementCountForRequest();
        return transmitPacket(packet, description, encap, to);
    }

@@ -937,6 +964,7 @@ public class DhcpClient extends StateMachine {
                    } else {
                        startInitRebootOrInit();
                    }
                    recordMetricEnabledFeatures();
                    return HANDLED;
                default:
                    return NOT_HANDLED;
@@ -1422,6 +1450,7 @@ public class DhcpClient extends StateMachine {
            try {
                final ArpPacket packet = ArpPacket.parseArpPacket(recvbuf, length);
                if (hasIpAddressConflict(packet, mTargetIp)) {
                    mMetrics.incrementCountForIpConflict();
                    sendMessage(EVENT_IP_CONFLICT);
                }
            } catch (ArpPacket.ParseException e) {
+51 −19
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.stats.connectivity.DisconnectCode;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -79,6 +80,7 @@ import com.android.internal.util.WakeupMessage;
import com.android.networkstack.apishim.NetworkInformationShimImpl;
import com.android.networkstack.apishim.common.NetworkInformationShim;
import com.android.networkstack.apishim.common.ShimUtils;
import com.android.networkstack.metrics.IpProvisioningMetrics;
import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService.NetworkStackServiceManager;

@@ -129,6 +131,7 @@ public class IpClient extends StateMachine {
    private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
    private final NetworkStackIpMemoryStore mIpMemoryStore;
    private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance();
    private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics();

    /**
     * Dump all state machine and connectivity packet logs to the specified writer.
@@ -527,8 +530,8 @@ public class IpClient extends StateMachine {
         * Get a DhcpClient Dependencies instance.
         */
        public DhcpClient.Dependencies getDhcpClientDependencies(
                NetworkStackIpMemoryStore ipMemoryStore) {
            return new DhcpClient.Dependencies(ipMemoryStore);
                NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) {
            return new DhcpClient.Dependencies(ipMemoryStore, metrics);
        }

        /**
@@ -818,7 +821,10 @@ public class IpClient extends StateMachine {
     * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
     */
    public void stop() {
        sendMessage(CMD_STOP);
        // The message "arg1" parameter is used to record the disconnect code metrics.
        // Usually this method is called by the peer (e.g. wifi) intentionally to stop IpClient,
        // consider that's the normal user termination.
        sendMessage(CMD_STOP, DisconnectCode.DC_NORMAL_TERMINATION.getNumber());
    }

    /**
@@ -1072,6 +1078,14 @@ public class IpClient extends StateMachine {
        mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
    }

    // Record the DisconnectCode and transition to StoppingState.
    // When jumping to mStoppingState This function will ensure
    // that you will not forget to fill in DisconnectCode.
    private void transitionToStoppingState(final DisconnectCode code) {
        mIpProvisioningMetrics.setDisconnectCode(code);
        transitionTo(mStoppingState);
    }

    // For now: use WifiStateMachine's historical notion of provisioned.
    @VisibleForTesting
    static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
@@ -1352,6 +1366,12 @@ public class IpClient extends StateMachine {
        if (Objects.equals(newLp, mLinkProperties)) {
            return true;
        }

        // Either success IPv4 or IPv6 provisioning triggers new LinkProperties update,
        // wait for the provisioning completion and record the latency.
        mIpProvisioningMetrics.setIPv4ProvisionedLatencyOnFirstTime(newLp.isIpv4Provisioned());
        mIpProvisioningMetrics.setIPv6ProvisionedLatencyOnFirstTime(newLp.isIpv6Provisioned());

        final int delta = setLinkProperties(newLp);
        // Most of the attributes stored in the memory store are deduced from
        // the link properties, therefore when the properties update the memory
@@ -1447,10 +1467,10 @@ public class IpClient extends StateMachine {
        }
        mCallback.onNewDhcpResults(null);

        handleProvisioningFailure();
        handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_FAIL);
    }

    private void handleProvisioningFailure() {
    private void handleProvisioningFailure(final DisconnectCode code) {
        final LinkProperties newLp = assembleLinkProperties();
        int delta = setLinkProperties(newLp);
        // If we've gotten here and we're still not provisioned treat that as
@@ -1467,7 +1487,7 @@ public class IpClient extends StateMachine {

        dispatchCallback(delta, newLp);
        if (delta == PROV_CHANGE_LOST_PROVISIONING) {
            transitionTo(mStoppingState);
            transitionToStoppingState(code);
        }
    }

@@ -1723,7 +1743,7 @@ public class IpClient extends StateMachine {
    private void startDhcpClient() {
        // Start DHCPv4.
        mDhcpClient = mDependencies.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
                mDependencies.getDhcpClientDependencies(mIpMemoryStore));
                mDependencies.getDhcpClientDependencies(mIpMemoryStore, mIpProvisioningMetrics));

        // If preconnection is enabled, there is no need to ask Wi-Fi to disable powersaving
        // during DHCP, because the DHCP handshake will happen during association. In order to
@@ -1744,7 +1764,8 @@ public class IpClient extends StateMachine {
            if (mInterfaceParams == null) {
                logError("Failed to find InterfaceParams for " + mInterfaceName);
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
                deferMessage(obtainMessage(CMD_STOP));
                deferMessage(obtainMessage(CMD_STOP,
                        DisconnectCode.DC_INTERFACE_NOT_FOUND.getNumber()));
                return;
            }

@@ -1836,6 +1857,7 @@ public class IpClient extends StateMachine {
    class StartedState extends State {
        @Override
        public void enter() {
            mIpProvisioningMetrics.reset();
            mStartTimeMillis = SystemClock.elapsedRealtime();
            if (mConfiguration.mProvisioningTimeoutMs > 0) {
                final long alarmTime = SystemClock.elapsedRealtime()
@@ -1847,13 +1869,17 @@ public class IpClient extends StateMachine {
        @Override
        public void exit() {
            mProvisioningTimeoutAlarm.cancel();

            // Record metrics information once this provisioning has completed due to certain
            // reason (normal termination, provisioning timeout, lost provisioning and etc).
            mIpProvisioningMetrics.statsWrite();
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_STOP:
                    transitionTo(mStoppingState);
                    transitionToStoppingState(DisconnectCode.forNumber(msg.arg1));
                    break;

                case CMD_UPDATE_L2KEY_CLUSTER: {
@@ -1875,7 +1901,7 @@ public class IpClient extends StateMachine {
                    break;

                case EVENT_PROVISIONING_TIMEOUT:
                    handleProvisioningFailure();
                    handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_TIMEOUT);
                    break;

                default:
@@ -1912,13 +1938,13 @@ public class IpClient extends StateMachine {

            if (mConfiguration.mEnableIPv6 && !startIPv6()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
                enqueueJumpToStoppingState();
                enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6);
                return;
            }

            if (mConfiguration.mEnableIPv4 && !isUsingPreconnection() && !startIPv4()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
                enqueueJumpToStoppingState();
                enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV4);
                return;
            }

@@ -1926,14 +1952,14 @@ public class IpClient extends StateMachine {
            if ((config != null) && !applyInitialConfig(config)) {
                // TODO introduce a new IpManagerEvent constant to distinguish this error case.
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
                enqueueJumpToStoppingState();
                enqueueJumpToStoppingState(DisconnectCode.DC_INVALID_PROVISIONING);
                return;
            }

            if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
                doImmediateProvisioningFailure(
                        IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
                enqueueJumpToStoppingState();
                enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPREACHABILITYMONITOR);
                return;
            }
        }
@@ -1965,8 +1991,8 @@ public class IpClient extends StateMachine {
            resetLinkProperties();
        }

        private void enqueueJumpToStoppingState() {
            deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING));
        private void enqueueJumpToStoppingState(final DisconnectCode code) {
            deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING, code.getNumber()));
        }

        private ConnectivityPacketTracker createPacketTracker() {
@@ -2001,7 +2027,7 @@ public class IpClient extends StateMachine {
            switch (msg.what) {
                case CMD_JUMP_RUNNING_TO_STOPPING:
                case CMD_STOP:
                    transitionTo(mStoppingState);
                    transitionToStoppingState(DisconnectCode.forNumber(msg.arg1));
                    break;

                case CMD_START:
@@ -2028,8 +2054,14 @@ public class IpClient extends StateMachine {
                    break;

                case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
                    // EVENT_NETLINK_LINKPROPERTIES_CHANGED message will be received in both of
                    // provisioning loss and normal user termination case (e.g. turn off wifi or
                    // switch to another wifi ssid), hence, checking current interface change
                    // status (down or up) would help distinguish.
                    final boolean ifUp = (msg.arg1 != 0);
                    if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
                        transitionTo(mStoppingState);
                        transitionToStoppingState(ifUp ? DisconnectCode.DC_PROVISIONING_FAIL
                                : DisconnectCode.DC_NORMAL_TERMINATION);
                    }
                    break;

@@ -2109,7 +2141,7 @@ public class IpClient extends StateMachine {
                    } else {
                        logError("Failed to set IPv4 address.");
                        dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, mLinkProperties);
                        transitionTo(mStoppingState);
                        transitionToStoppingState(DisconnectCode.DC_PROVISIONING_FAIL);
                    }
                    break;
                }
+18 −0
Original line number Diff line number Diff line
@@ -436,4 +436,22 @@ public class NetworkStackUtils {
        return addr instanceof Inet6Address
                && ((addr.getAddress()[0] & 0xfe) == 0xfc);
    }

    /**
     * Returns the {@code int} nearest in value to {@code value}.
     *
     * @param value any {@code long} value
     * @return the same value cast to {@code int} if it is in the range of the {@code int}
     * type, {@link Integer#MAX_VALUE} if it is too large, or {@link Integer#MIN_VALUE} if
     * it is too small
     */
    public static int saturatedCast(long value) {
        if (value > Integer.MAX_VALUE) {
            return Integer.MAX_VALUE;
        }
        if (value < Integer.MIN_VALUE) {
            return Integer.MIN_VALUE;
        }
        return (int) value;
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -48,6 +48,14 @@ public class Stopwatch {
        return this;
    }

    /**
     * Retart the Stopwatch.
     */
    public Stopwatch restart() {
        mStartTimeNs = SystemClock.elapsedRealtimeNanos();
        return this;
    }

    /**
     * Stop the Stopwatch.
     * @return the total time recorded, in microseconds, or 0 if not started.
+163 −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 android.net.util.NetworkStackUtils;
import android.net.util.Stopwatch;
import android.stats.connectivity.DhcpErrorCode;
import android.stats.connectivity.DhcpFeature;
import android.stats.connectivity.DisconnectCode;
import android.stats.connectivity.HostnameTransResult;

import java.util.HashSet;
import java.util.Set;

/**
 * Class to record the network IpProvisioning into statsd.
 * 1. Fill in NetworkIpProvisioningReported proto.
 * 2. Write the NetworkIpProvisioningReported proto into statsd.
 * 3. This class is not thread-safe, and should always be accessed from the same thread.
 * @hide
 */

public class IpProvisioningMetrics {
    private static final String TAG = IpProvisioningMetrics.class.getSimpleName();
    private final NetworkIpProvisioningReported.Builder mStatsBuilder =
            NetworkIpProvisioningReported.newBuilder();
    private final DhcpSession.Builder mDhcpSessionBuilder = DhcpSession.newBuilder();
    private final Stopwatch mIpv4Watch = new Stopwatch().start();
    private final Stopwatch mIpv6Watch = new Stopwatch().start();
    private final Stopwatch mWatch = new Stopwatch().start();
    private final Set<DhcpFeature> mDhcpFeatures = new HashSet<DhcpFeature>();

    // Define a maximum number of the DhcpErrorCode.
    public static final int MAX_DHCP_ERROR_COUNT = 20;

    /**
     *  reset this all metrics members
     */
    public void reset() {
        mStatsBuilder.clear();
        mDhcpSessionBuilder.clear();
        mDhcpFeatures.clear();
        mIpv4Watch.restart();
        mIpv6Watch.restart();
        mWatch.restart();
    }

    /**
     * Write the TransportType into mStatsBuilder.
     * TODO: implement this
     */
    public void setTransportType() {}

    /**
     * Write the IPv4Provisioned latency into mStatsBuilder.
     */
    public void setIPv4ProvisionedLatencyOnFirstTime(final boolean isIpv4Provisioned) {
        if (isIpv4Provisioned && !mStatsBuilder.hasIpv4LatencyMicros()) {
            mStatsBuilder.setIpv4LatencyMicros(NetworkStackUtils.saturatedCast(mIpv4Watch.stop()));
        }
    }

    /**
     * Write the IPv6Provisioned latency into mStatsBuilder.
     */
    public void setIPv6ProvisionedLatencyOnFirstTime(final boolean isIpv6Provisioned) {
        if (isIpv6Provisioned && !mStatsBuilder.hasIpv6LatencyMicros()) {
            mStatsBuilder.setIpv6LatencyMicros(NetworkStackUtils.saturatedCast(mIpv6Watch.stop()));
        }
    }

    /**
     * Write the DhcpFeature proto into mStatsBuilder.
     */
    public void setDhcpEnabledFeature(final DhcpFeature feature) {
        if (feature == DhcpFeature.DF_UNKNOWN) return;
        mDhcpFeatures.add(feature);
    }

    /**
     * Write the DHCPDISCOVER transmission count into DhcpSession.
     */
    public void incrementCountForDiscover() {
        mDhcpSessionBuilder.setDiscoverCount(mDhcpSessionBuilder.getDiscoverCount() + 1);
    }

    /**
     * Write the DHCPREQUEST transmission count into DhcpSession.
     */
    public void incrementCountForRequest() {
        mDhcpSessionBuilder.setRequestCount(mDhcpSessionBuilder.getRequestCount() + 1);
    }

    /**
     * Write the IPv4 address conflict count into DhcpSession.
     */
    public void incrementCountForIpConflict() {
        mDhcpSessionBuilder.setConflictCount(mDhcpSessionBuilder.getConflictCount() + 1);
    }

    /**
     * Write the hostname transliteration result into DhcpSession.
     */
    public void setHostnameTransinfo(final boolean isOptionEnabled, final boolean transSuccess) {
        mDhcpSessionBuilder.setHtResult(!isOptionEnabled ? HostnameTransResult.HTR_DISABLE :
                transSuccess ? HostnameTransResult.HTR_SUCCESS : HostnameTransResult.HTR_FAILURE);
    }

    /**
     * write the DHCP error code into DhcpSession.
     */
    public void addDhcpErrorCode(final int errorCode) {
        if (mDhcpSessionBuilder.getErrorCodeCount() >= MAX_DHCP_ERROR_COUNT) return;
        mDhcpSessionBuilder.addErrorCode(DhcpErrorCode.forNumber(errorCode));
    }

    /**
     * Write the IP provision disconnect code into DhcpSession.
     */
    public void setDisconnectCode(final DisconnectCode disconnectCode) {
        if (mStatsBuilder.hasDisconnectCode()) return;
        mStatsBuilder.setDisconnectCode(disconnectCode);
    }

    /**
     * Write the NetworkIpProvisioningReported proto into statsd.
     */
    public NetworkIpProvisioningReported statsWrite() {
        if (!mWatch.isStarted()) return null;
        for (DhcpFeature feature : mDhcpFeatures) {
            mDhcpSessionBuilder.addUsedFeatures(feature);
        }
        mStatsBuilder.setDhcpSession(mDhcpSessionBuilder);
        mStatsBuilder.setProvisioningDurationMicros(mWatch.stop());
        mStatsBuilder.setRandomNumber((int) (Math.random() * 1000));
        final NetworkIpProvisioningReported Stats = mStatsBuilder.build();
        final byte[] DhcpSession = Stats.getDhcpSession().toByteArray();
        NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_IP_PROVISIONING_REPORTED,
                Stats.getTransportType().getNumber(),
                Stats.getIpv4LatencyMicros(),
                Stats.getIpv6LatencyMicros(),
                Stats.getProvisioningDurationMicros(),
                Stats.getDisconnectCode().getNumber(),
                DhcpSession,
                Stats.getRandomNumber());
        mWatch.reset();
        return Stats;
    }
}
Loading