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

Commit d9d3bccc authored by Xiao Ma's avatar Xiao Ma
Browse files

Add IpReachabilityMonitor metrics.

Bug: 162944199
Test: atest NetworkStackTests
Change-Id: I5320905a562000180343fc2d19fc4352210bb393
parent 246ed712
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1904,7 +1904,7 @@ public class IpClient extends StateMachine {
        }

        if (mIpReachabilityMonitor != null) {
            mIpReachabilityMonitor.probeAll();
            mIpReachabilityMonitor.probeAll(true /* dueToRoam */);
        }

        // Check whether to refresh previous IP lease on L2 roaming happened.
@@ -2379,7 +2379,7 @@ public class IpClient extends StateMachine {
                    // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
                    // roams.
                    if (mIpReachabilityMonitor != null) {
                        mIpReachabilityMonitor.probeAll();
                        mIpReachabilityMonitor.probeAll(false /* dueToRoam */);
                    }
                    break;

+103 −6
Original line number Diff line number Diff line
@@ -41,6 +41,9 @@ import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.os.SystemClock;
import android.stats.connectivity.IpType;
import android.stats.connectivity.NudEventType;
import android.stats.connectivity.NudNeighborType;
import android.text.TextUtils;
import android.util.Log;

@@ -52,6 +55,7 @@ import com.android.internal.util.Preconditions;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.netlink.StructNdMsg;
import com.android.networkstack.R;
import com.android.networkstack.metrics.IpReachabilityMonitorMetrics;

import java.io.PrintWriter;
import java.net.Inet6Address;
@@ -170,6 +174,7 @@ public class IpReachabilityMonitor {
        void acquireWakeLock(long durationMs);
        IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb);
        boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled);
        IpReachabilityMonitorMetrics getIpReachabilityMonitorMetrics();

        static Dependencies makeDefault(Context context, String iface) {
            final String lockName = TAG + "." + iface;
@@ -191,6 +196,10 @@ public class IpReachabilityMonitor {
                    return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
                            defaultEnabled);
                }

                public IpReachabilityMonitorMetrics getIpReachabilityMonitorMetrics() {
                    return new IpReachabilityMonitorMetrics();
                }
            };
        }
    }
@@ -204,10 +213,14 @@ public class IpReachabilityMonitor {
    private final IpConnectivityLog mMetricsLog;
    private final Context mContext;
    private final INetd mNetd;
    private final IpReachabilityMonitorMetrics mIpReachabilityMetrics;
    private LinkProperties mLinkProperties = new LinkProperties();
    private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
    // Time in milliseconds of the last forced probe request.
    private volatile long mLastProbeTimeMs;
    // Time in milliseconds of the last forced probe request due to roam or CMD_CONFIRM.
    private long mLastProbeDueToRoamMs;
    private long mLastProbeDueToConfirmMs;
    private int mNumSolicits;
    private int mInterSolicitIntervalMs;
    @NonNull
@@ -269,6 +282,7 @@ public class IpReachabilityMonitor {
                    }
                });
        mIpNeighborMonitor.start();
        mIpReachabilityMetrics = dependencies.getIpReachabilityMonitorMetrics();
    }

    public void stop() {
@@ -332,6 +346,13 @@ public class IpReachabilityMonitor {
        return false;
    }

    private boolean isNeighborDnsServer(@NonNull final NeighborEvent event) {
        for (InetAddress dns : mLinkProperties.getDnsServers()) {
            if (event.ip.equals(dns)) return true;
        }
        return false;
    }

    private boolean isMulticastResolicitEnabled() {
        return mDependencies.isFeatureEnabled(mContext, IP_REACHABILITY_MCAST_RESOLICIT_VERSION,
                false /* defaultEnabled */);
@@ -433,7 +454,7 @@ public class IpReachabilityMonitor {
            // an InetAddress argument.
            mCallback.notifyLost(ip, logMsg);
        }
        logNudFailed(lostProvisioning);
        logNudFailed(event, lostProvisioning);
    }

    private void maybeRestoreNeighborParameters() {
@@ -457,7 +478,13 @@ public class IpReachabilityMonitor {
        return !mUsingMultinetworkPolicyTracker || mCm.shouldAvoidBadWifi();
    }

    public void probeAll() {
    /**
     * Force probe to verify whether or not the critical on-link neighbours are still reachable.
     *
     * @param dueToRoam indicate on which situation forced probe has been sent, e.g., on post
     *                  roaming or receiving CMD_CONFIRM from IpClient.
     */
    public void probeAll(boolean dueToRoam) {
        setNeighbourParametersPostRoaming();

        final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
@@ -478,6 +505,11 @@ public class IpReachabilityMonitor {
            logEvent(IpReachabilityEvent.PROBE, rval);
        }
        mLastProbeTimeMs = SystemClock.elapsedRealtime();
        if (dueToRoam) {
            mLastProbeDueToRoamMs = mLastProbeTimeMs;
        } else {
            mLastProbeDueToConfirmMs = mLastProbeTimeMs;
        }
    }

    private long getProbeWakeLockDuration() {
@@ -537,16 +569,81 @@ public class IpReachabilityMonitor {
        mInterSolicitIntervalMs = interSolicitIntervalMs;
    }

    private boolean isFromProbe() {
        final long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
        return duration < getProbeWakeLockDuration();
    }

    private boolean isProbedNudFailureDueToRoam() {
        // Check to which probe expiry the curren timestamp gets close when NUD failure event
        // happens, theoretically that indicates which probe event(due to roam or CMD_CONFIRM)
        // was triggered eariler.
        //
        // Note that this would be incorrect if the probe or confirm was so long ago that the
        // probe duration has already expired. That cannot happen because isFromProbe would return
        // false, and this method is only called on NUD failures due to probes.
        final long probeExpiryAfterRoam = mLastProbeDueToRoamMs + getProbeWakeLockDuration();
        final long probeExpiryAfterConfirm =
                mLastProbeDueToConfirmMs + getProbeWakeLockDuration();
        final long currentTime = SystemClock.elapsedRealtime();
        return Math.abs(probeExpiryAfterRoam - currentTime)
                < Math.abs(probeExpiryAfterConfirm - currentTime);
    }

    private void logEvent(int probeType, int errorCode) {
        int eventType = probeType | (errorCode & 0xff);
        mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
    }

    private void logNudFailed(boolean lostProvisioning) {
        long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
        boolean isFromProbe = (duration < getProbeWakeLockDuration());
        int eventType = nudFailureEventType(isFromProbe, lostProvisioning);
    private void logNudFailed(final NeighborEvent event, boolean lostProvisioning) {
        final int eventType = nudFailureEventType(isFromProbe(), lostProvisioning);
        mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
        logNeighborLostEvent(event, lostProvisioning);
    }

    /**
     * Returns the neighbor type code corresponding to the given conditions.
     */
    private NudNeighborType getNeighborType(final NeighborEvent event) {
        final boolean isGateway = isNeighborDefaultRouter(event);
        final boolean isDnsServer = isNeighborDnsServer(event);

        if (isGateway && isDnsServer) return NudNeighborType.NUD_NEIGHBOR_BOTH;
        if (isGateway && !isDnsServer) return NudNeighborType.NUD_NEIGHBOR_GATEWAY;
        if (!isGateway && isDnsServer) return NudNeighborType.NUD_NEIGHBOR_DNS;
        return NudNeighborType.NUD_NEIGHBOR_UNKNOWN;
    }

    /**
     * Returns the NUD failure event type code corresponding to the given conditions.
     */
    private static NudEventType getNudFailureEventType(boolean isFromProbe, boolean isDueToRoam,
            boolean isProvisioningLost) {
        if (!isFromProbe) {
            return isProvisioningLost
                    ? NudEventType.NUD_ORGANIC_FAILED_CRITICAL
                    : NudEventType.NUD_ORGANIC_FAILED;
        }
        return isProvisioningLost
                ? isDueToRoam
                        ? NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL
                        : NudEventType.NUD_CONFIRM_FAILED_CRITICAL
                : isDueToRoam
                        ? NudEventType.NUD_POST_ROAMING_FAILED
                        : NudEventType.NUD_CONFIRM_FAILED;
    }

    /**
     * Log NUD failure metrics with new Westworld APIs while the function using mMetricsLog API
     * still sends the legacy metrics, @see #logNudFailed.
     */
    private void logNeighborLostEvent(final NeighborEvent event, boolean isProvisioningLost) {
        final IpType ipType = (event.ip instanceof Inet6Address) ? IpType.IPV6 : IpType.IPV4;
        mIpReachabilityMetrics.setNudIpType(ipType);
        mIpReachabilityMetrics.setNudNeighborType(getNeighborType(event));
        mIpReachabilityMetrics.setNudEventType(getNudFailureEventType(isFromProbe(),
                isProbedNudFailureDueToRoam(), isProvisioningLost));
        mIpReachabilityMetrics.statsWrite();
    }

    /**
+9 −9
Original line number Diff line number Diff line
@@ -157,17 +157,17 @@ public class IpProvisioningMetrics {
        mStatsBuilder.setDhcpSession(mDhcpSessionBuilder);
        mStatsBuilder.setProvisioningDurationMicros(mWatch.stop());
        mStatsBuilder.setRandomNumber((int) (Math.random() * 1000));
        final NetworkIpProvisioningReported Stats = mStatsBuilder.build();
        final byte[] DhcpSession = Stats.getDhcpSession().toByteArray();
        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(),
                stats.getTransportType().getNumber(),
                stats.getIpv4LatencyMicros(),
                stats.getIpv6LatencyMicros(),
                stats.getProvisioningDurationMicros(),
                stats.getDisconnectCode().getNumber(),
                DhcpSession,
                Stats.getRandomNumber());
                stats.getRandomNumber());
        mWatch.reset();
        return Stats;
        return stats;
    }
}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.stats.connectivity.IpType;
import android.stats.connectivity.NudEventType;
import android.stats.connectivity.NudNeighborType;

/**
 * Class to record the network stack IpReachabilityMonitor metrics into statsd.
 *
 * This class is not thread-safe, and should always be accessed from the same thread.
 *
 * @hide
 */
public class IpReachabilityMonitorMetrics {
    private final NetworkIpReachabilityMonitorReported.Builder mStatsBuilder =
            NetworkIpReachabilityMonitorReported.newBuilder();

    /**
     * Write the NUD event type into mStatsBuilder.
     */
    public void setNudEventType(final NudEventType type) {
        mStatsBuilder.setEventType(type);
    }

    /**
     * Write the NUD probe type(IPv4 or IPv6) into mStatsBuilder.
     */
    public void setNudIpType(final IpType type) {
        mStatsBuilder.setIpType(type);
    }

    /**
     * Write the NUD probe neighbor type into mStatsBuilder.
     */
    public void setNudNeighborType(final NudNeighborType type) {
        mStatsBuilder.setNeighborType(type);
    }

    /**
     * Write the NetworkIpReachabilityMonitorReported proto into statsd.
     */
    public NetworkIpReachabilityMonitorReported statsWrite() {
        final NetworkIpReachabilityMonitorReported stats = mStatsBuilder.build();
        NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_IP_REACHABILITY_MONITOR_REPORTED,
                stats.getEventType().getNumber(),
                stats.getIpType().getNumber(),
                stats.getNeighborType().getNumber());
        return stats;
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -172,3 +172,19 @@ message NetworkStackQuirkReported {
    // Record each Quirk event
    optional .android.stats.connectivity.NetworkQuirkEvent event = 2;
}

/**
 * Logs Neighbor Unreachability Detection probe event.
 * Logged from:
 * src/com/android/networkstack/metrics/IpReachabilityMonitorMetrics.java
 */
message NetworkIpReachabilityMonitorReported {
    // Neighbor Unreachability Detection event.
    optional .android.stats.connectivity.NudEventType event_type = 1;

    // NUD probe based on IPv4 ARP or IPv6 ND packet.
    optional .android.stats.connectivity.IpType ip_type = 2;

    // NUD neighbor type, default gateway, DNS server or both.
    optional .android.stats.connectivity.NudNeighborType neighbor_type = 3;
}
Loading