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

Commit 22d9b2d4 authored by Hugo Benichi's avatar Hugo Benichi
Browse files

IP connectivity metrics: add new APF counters.

This patch adds a few missing counters to APF events:
 - an actual lifetime duration to ApfProgramEvent.
 - counters for total number of updates to ApfStatistics.

ApfProgramEvents are now recorded at program removal in order to
populate the actual lifetime of the program. ApfProgramEvents whose
actual lifetime was less than 1 second are filtered out.

Finally, instance fields of ApfProgramEvent and ApfStats classes are
made mutable to allow for simple record-like creation. This was not
possible when these classes were tagged @SystemApi.

Test: - manually verified output of $ dumpsys connmetrics list
      - unit tests updated.
Bug: 34901696

Change-Id: I02694ebb9421ce1c2aa757fa6aa209d19a654dcd
parent dc159ee5
Loading
Loading
Loading
Loading
+12 −15
Original line number Diff line number Diff line
@@ -47,23 +47,19 @@ public final class ApfProgramEvent implements Parcelable {
    @Retention(RetentionPolicy.SOURCE)
    public @interface Flags {}

    public final long lifetime;     // Lifetime of the program in seconds
    public final int filteredRas;   // Number of RAs filtered by the APF program
    public final int currentRas;    // Total number of current RAs at generation time
    public final int programLength; // Length of the APF program in bytes
    public final int flags;         // Bitfield compound of FLAG_* constants

    public ApfProgramEvent(
            long lifetime, int filteredRas, int currentRas, int programLength, @Flags int flags) {
        this.lifetime = lifetime;
        this.filteredRas = filteredRas;
        this.currentRas = currentRas;
        this.programLength = programLength;
        this.flags = flags;
    public long lifetime;       // Maximum computed lifetime of the program in seconds
    public long actualLifetime; // Effective program lifetime in seconds
    public int filteredRas;     // Number of RAs filtered by the APF program
    public int currentRas;      // Total number of current RAs at generation time
    public int programLength;   // Length of the APF program in bytes
    public int flags;           // Bitfield compound of FLAG_* constants

    public ApfProgramEvent() {
    }

    private ApfProgramEvent(Parcel in) {
        this.lifetime = in.readLong();
        this.actualLifetime = in.readLong();
        this.filteredRas = in.readInt();
        this.currentRas = in.readInt();
        this.programLength = in.readInt();
@@ -73,6 +69,7 @@ public final class ApfProgramEvent implements Parcelable {
    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeLong(lifetime);
        out.writeLong(actualLifetime);
        out.writeInt(filteredRas);
        out.writeInt(currentRas);
        out.writeInt(programLength);
@@ -87,8 +84,8 @@ public final class ApfProgramEvent implements Parcelable {
    @Override
    public String toString() {
        String lifetimeString = (lifetime < Long.MAX_VALUE) ? lifetime + "s" : "forever";
        return String.format("ApfProgramEvent(%d/%d RAs %dB %s %s)",
                filteredRas, currentRas, programLength, lifetimeString, namesOf(flags));
        return String.format("ApfProgramEvent(%d/%d RAs %dB %ds/%s %s)", filteredRas, currentRas,
                programLength, actualLifetime, lifetimeString, namesOf(flags));
    }

    public static final Parcelable.Creator<ApfProgramEvent> CREATOR
+28 −20
Original line number Diff line number Diff line
@@ -25,25 +25,28 @@ import android.os.Parcelable;
 */
public final class ApfStats implements Parcelable {

    public final long durationMs;     // time interval in milliseconds these stastistics covers
    public final int receivedRas;     // number of received RAs
    public final int matchingRas;     // number of received RAs matching a known RA
    public final int droppedRas;      // number of received RAs ignored due to the MAX_RAS limit
    public final int zeroLifetimeRas; // number of received RAs with a minimum lifetime of 0
    public final int parseErrors;     // number of received RAs that could not be parsed
    public final int programUpdates;  // number of APF program updates
    public final int maxProgramSize;  // maximum APF program size advertised by hardware
    /** time interval in milliseconds these stastistics covers. */
    public long durationMs;
    /** number of received RAs. */
    public int receivedRas;
    /** number of received RAs matching a known RA. */
    public int matchingRas;
    /** number of received RAs ignored due to the MAX_RAS limit. */
    public int droppedRas;
    /** number of received RAs with a minimum lifetime of 0. */
    public int zeroLifetimeRas;
    /** number of received RAs that could not be parsed. */
    public int parseErrors;
    /** number of APF program updates from receiving RAs.. */
    public int programUpdates;
    /** total number of APF program updates. */
    public int programUpdatesAll;
    /** number of APF program updates from allowing multicast traffic. */
    public int programUpdatesAllowingMulticast;
    /** maximum APF program size advertised by hardware. */
    public int maxProgramSize;

    public ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas,
            int zeroLifetimeRas, int parseErrors, int programUpdates, int maxProgramSize) {
        this.durationMs = durationMs;
        this.receivedRas = receivedRas;
        this.matchingRas = matchingRas;
        this.droppedRas = droppedRas;
        this.zeroLifetimeRas = zeroLifetimeRas;
        this.parseErrors = parseErrors;
        this.programUpdates = programUpdates;
        this.maxProgramSize = maxProgramSize;
    public ApfStats() {
    }

    private ApfStats(Parcel in) {
@@ -54,6 +57,8 @@ public final class ApfStats implements Parcelable {
        this.zeroLifetimeRas = in.readInt();
        this.parseErrors = in.readInt();
        this.programUpdates = in.readInt();
        this.programUpdatesAll = in.readInt();
        this.programUpdatesAllowingMulticast = in.readInt();
        this.maxProgramSize = in.readInt();
    }

@@ -66,6 +71,8 @@ public final class ApfStats implements Parcelable {
        out.writeInt(zeroLifetimeRas);
        out.writeInt(parseErrors);
        out.writeInt(programUpdates);
        out.writeInt(programUpdatesAll);
        out.writeInt(programUpdatesAllowingMulticast);
        out.writeInt(maxProgramSize);
    }

@@ -83,8 +90,9 @@ public final class ApfStats implements Parcelable {
                .append(String.format("%d matching, ", matchingRas))
                .append(String.format("%d dropped, ", droppedRas))
                .append(String.format("%d zero lifetime, ", zeroLifetimeRas))
                .append(String.format("%d parse errors, ", parseErrors))
                .append(String.format("%d program updates})", programUpdates))
                .append(String.format("%d parse errors}, ", parseErrors))
                .append(String.format("updates: {all: %d, RAs: %d, allow multicast: %d})",
                        programUpdatesAll, programUpdates, programUpdatesAllowingMulticast))
                .toString();
    }

+19 −8
Original line number Diff line number Diff line
@@ -151,7 +151,8 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setDnsEvent(IpConnectivityEvent out, DnsEvent in) {
        IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch = new IpConnectivityLogClass.DNSLookupBatch();
        IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch =
                new IpConnectivityLogClass.DNSLookupBatch();
        dnsLookupBatch.networkId = netIdOf(in.netId);
        dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes);
        dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes);
@@ -160,7 +161,8 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setIpManagerEvent(IpConnectivityEvent out, IpManagerEvent in) {
        IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent = new IpConnectivityLogClass.IpProvisioningEvent();
        IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent =
                new IpConnectivityLogClass.IpProvisioningEvent();
        ipProvisioningEvent.ifName = in.ifName;
        ipProvisioningEvent.eventType = in.eventType;
        ipProvisioningEvent.latencyMs = (int) in.durationMs;
@@ -168,14 +170,16 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setIpReachabilityEvent(IpConnectivityEvent out, IpReachabilityEvent in) {
        IpConnectivityLogClass.IpReachabilityEvent ipReachabilityEvent = new IpConnectivityLogClass.IpReachabilityEvent();
        IpConnectivityLogClass.IpReachabilityEvent ipReachabilityEvent =
                new IpConnectivityLogClass.IpReachabilityEvent();
        ipReachabilityEvent.ifName = in.ifName;
        ipReachabilityEvent.eventType = in.eventType;
        out.setIpReachabilityEvent(ipReachabilityEvent);
    }

    private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) {
        IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent = new IpConnectivityLogClass.DefaultNetworkEvent();
        IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent =
                new IpConnectivityLogClass.DefaultNetworkEvent();
        defaultNetworkEvent.networkId = netIdOf(in.netId);
        defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId);
        defaultNetworkEvent.transportTypes = in.transportTypes;
@@ -184,7 +188,8 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
        IpConnectivityLogClass.NetworkEvent networkEvent = new IpConnectivityLogClass.NetworkEvent();
        IpConnectivityLogClass.NetworkEvent networkEvent =
                new IpConnectivityLogClass.NetworkEvent();
        networkEvent.networkId = netIdOf(in.netId);
        networkEvent.eventType = in.eventType;
        networkEvent.latencyMs = (int) in.durationMs;
@@ -192,7 +197,8 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setValidationProbeEvent(IpConnectivityEvent out, ValidationProbeEvent in) {
        IpConnectivityLogClass.ValidationProbeEvent validationProbeEvent = new IpConnectivityLogClass.ValidationProbeEvent();
        IpConnectivityLogClass.ValidationProbeEvent validationProbeEvent =
                new IpConnectivityLogClass.ValidationProbeEvent();
        validationProbeEvent.networkId = netIdOf(in.netId);
        validationProbeEvent.latencyMs = (int) in.durationMs;
        validationProbeEvent.probeType = in.probeType;
@@ -201,8 +207,10 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setApfProgramEvent(IpConnectivityEvent out, ApfProgramEvent in) {
        IpConnectivityLogClass.ApfProgramEvent apfProgramEvent = new IpConnectivityLogClass.ApfProgramEvent();
        IpConnectivityLogClass.ApfProgramEvent apfProgramEvent =
                new IpConnectivityLogClass.ApfProgramEvent();
        apfProgramEvent.lifetime = in.lifetime;
        apfProgramEvent.effectiveLifetime = in.actualLifetime;
        apfProgramEvent.filteredRas = in.filteredRas;
        apfProgramEvent.currentRas = in.currentRas;
        apfProgramEvent.programLength = in.programLength;
@@ -216,7 +224,8 @@ final public class IpConnectivityEventBuilder {
    }

    private static void setApfStats(IpConnectivityEvent out, ApfStats in) {
        IpConnectivityLogClass.ApfStatistics apfStatistics = new IpConnectivityLogClass.ApfStatistics();
        IpConnectivityLogClass.ApfStatistics apfStatistics =
                new IpConnectivityLogClass.ApfStatistics();
        apfStatistics.durationMs = in.durationMs;
        apfStatistics.receivedRas = in.receivedRas;
        apfStatistics.matchingRas = in.matchingRas;
@@ -224,6 +233,8 @@ final public class IpConnectivityEventBuilder {
        apfStatistics.zeroLifetimeRas = in.zeroLifetimeRas;
        apfStatistics.parseErrors = in.parseErrors;
        apfStatistics.programUpdates = in.programUpdates;
        apfStatistics.programUpdatesAll = in.programUpdatesAll;
        apfStatistics.programUpdatesAllowingMulticast = in.programUpdatesAllowingMulticast;
        apfStatistics.maxProgramSize = in.maxProgramSize;
        out.setApfStatistics(apfStatistics);
    }
+59 −31
Original line number Diff line number Diff line
@@ -93,17 +93,10 @@ public class ApfFilter {
    class ReceiveThread extends Thread {
        private final byte[] mPacket = new byte[1514];
        private final FileDescriptor mSocket;
        private volatile boolean mStopped;

        // Starting time of the RA receiver thread.
        private final long mStart = SystemClock.elapsedRealtime();
        private final ApfStats mStats = new ApfStats();

        private int mReceivedRas;     // Number of received RAs
        private int mMatchingRas;     // Number of received RAs matching a known RA
        private int mDroppedRas;      // Number of received RAs ignored due to the MAX_RAS limit
        private int mParseErrors;     // Number of received RAs that could not be parsed
        private int mZeroLifetimeRas; // Number of received RAs with a 0 lifetime
        private int mProgramUpdates;  // Number of APF program updates triggered by receiving a RA
        private volatile boolean mStopped;

        public ReceiveThread(FileDescriptor socket) {
            mSocket = socket;
@@ -134,35 +127,40 @@ public class ApfFilter {
        }

        private void updateStats(ProcessRaResult result) {
            mReceivedRas++;
            mStats.receivedRas++;
            switch(result) {
                case MATCH:
                    mMatchingRas++;
                    mStats.matchingRas++;
                    return;
                case DROPPED:
                    mDroppedRas++;
                    mStats.droppedRas++;
                    return;
                case PARSE_ERROR:
                    mParseErrors++;
                    mStats.parseErrors++;
                    return;
                case ZERO_LIFETIME:
                    mZeroLifetimeRas++;
                    mStats.zeroLifetimeRas++;
                    return;
                case UPDATE_EXPIRY:
                    mMatchingRas++;
                    mProgramUpdates++;
                    mStats.matchingRas++;
                    mStats.programUpdates++;
                    return;
                case UPDATE_NEW_RA:
                    mProgramUpdates++;
                    mStats.programUpdates++;
                    return;
            }
        }

        private void logStats() {
            long durationMs = SystemClock.elapsedRealtime() - mStart;
            int maxSize = mApfCapabilities.maximumApfProgramSize;
            mMetricsLog.log(new ApfStats(durationMs, mReceivedRas, mMatchingRas, mDroppedRas,
                     mZeroLifetimeRas, mParseErrors, mProgramUpdates, maxSize));
            final long nowMs = SystemClock.elapsedRealtime();
            synchronized (this) {
                mStats.durationMs = nowMs - mStart;
                mStats.maxProgramSize = mApfCapabilities.maximumApfProgramSize;
                mStats.programUpdatesAll = mNumProgramUpdates;
                mStats.programUpdatesAllowingMulticast = mNumProgramUpdatesAllowingMulticast;
                mMetricsLog.log(mStats);
                logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
            }
        }
    }

@@ -218,6 +216,8 @@ public class ApfFilter {
            4,    // Protocol size: 4
    };
    private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
    // Do not log ApfProgramEvents whose actual lifetimes was less than this.
    private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;

    private final ApfCapabilities mApfCapabilities;
    private final IpManager.Callback mIpManagerCallback;
@@ -247,6 +247,7 @@ public class ApfFilter {
        mMulticastFilter = multicastFilter;
        mMetricsLog = log;

        // TODO: ApfFilter should not generate programs until IpManager sends provisioning success.
        maybeStartFilter();
    }

@@ -661,14 +662,19 @@ public class ApfFilter {
    // How long should the last installed filter program live for? In seconds.
    @GuardedBy("this")
    private long mLastInstalledProgramMinLifetime;
    @GuardedBy("this")
    private ApfProgramEvent mLastInstallEvent;

    // For debugging only. The last program installed.
    @GuardedBy("this")
    private byte[] mLastInstalledProgram;

    // For debugging only. How many times the program was updated since we started.
    // How many times the program was updated since we started.
    @GuardedBy("this")
    private int mNumProgramUpdates = 0;
    // How many times the program was updated since we started for allowing multicast traffic.
    @GuardedBy("this")
    private int mNumProgramUpdates;
    private int mNumProgramUpdatesAllowingMulticast = 0;

    /**
     * Generate filter code to process ARP packets. Execution of this code ends in either the
@@ -947,7 +953,8 @@ public class ApfFilter {
            Log.e(TAG, "Failed to generate APF program.", e);
            return;
        }
        mLastTimeInstalledProgram = currentTimeSeconds();
        final long now = currentTimeSeconds();
        mLastTimeInstalledProgram = now;
        mLastInstalledProgramMinLifetime = programMinLifetime;
        mLastInstalledProgram = program;
        mNumProgramUpdates++;
@@ -956,9 +963,26 @@ public class ApfFilter {
            hexDump("Installing filter: ", program, program.length);
        }
        mIpManagerCallback.installPacketFilter(program);
        int flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
        mMetricsLog.log(new ApfProgramEvent(
                programMinLifetime, rasToFilter.size(), mRas.size(), program.length, flags));
        logApfProgramEventLocked(now);
        mLastInstallEvent = new ApfProgramEvent();
        mLastInstallEvent.lifetime = programMinLifetime;
        mLastInstallEvent.filteredRas = rasToFilter.size();
        mLastInstallEvent.currentRas = mRas.size();
        mLastInstallEvent.programLength = program.length;
        mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
    }

    private void logApfProgramEventLocked(long now) {
        if (mLastInstallEvent == null) {
            return;
        }
        ApfProgramEvent ev = mLastInstallEvent;
        mLastInstallEvent = null;
        ev.actualLifetime = now - mLastTimeInstalledProgram;
        if (ev.actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
            return;
        }
        mMetricsLog.log(ev);
    }

    /**
@@ -1078,11 +1102,15 @@ public class ApfFilter {
        mRas.clear();
    }

    public synchronized void setMulticastFilter(boolean enabled) {
        if (mMulticastFilter != enabled) {
            mMulticastFilter = enabled;
            installNewProgramLocked();
    public synchronized void setMulticastFilter(boolean isEnabled) {
        if (mMulticastFilter == isEnabled) {
            return;
        }
        mMulticastFilter = isEnabled;
        if (!isEnabled) {
            mNumProgramUpdatesAllowingMulticast++;
        }
        installNewProgramLocked();
    }

    /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
+6 −3
Original line number Diff line number Diff line
@@ -304,6 +304,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(ApfProgramEvent.class),
                aLong(200),
                aLong(18),
                anInt(7),
                anInt(9),
                anInt(2048),
@@ -320,7 +321,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
                "  apf_program_event <",
                "    current_ras: 9",
                "    drop_multicast: true",
                "    effective_lifetime: 0",
                "    effective_lifetime: 18",
                "    filtered_ras: 7",
                "    has_ipv4_addr: true",
                "    lifetime: 200",
@@ -343,6 +344,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
                anInt(1),
                anInt(2),
                anInt(4),
                anInt(7),
                anInt(3),
                anInt(2048));

        String want = joinLines(
@@ -360,8 +363,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
                "    max_program_size: 2048",
                "    parse_errors: 2",
                "    program_updates: 4",
                "    program_updates_all: 0",
                "    program_updates_allowing_multicast: 0",
                "    program_updates_all: 7",
                "    program_updates_allowing_multicast: 3",
                "    received_ras: 10",
                "    zero_lifetime_ras: 1",
                "  >",
Loading