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

Commit 73fd4d1f authored by Hugo Benichi's avatar Hugo Benichi
Browse files

Connectivity metrics: serialize networkId, transports, ifname

This patch adds translation from ConnectivityMetricsEvent to
IpConnectivityEvent of recently added fields:
 - top-level network id
 - top-level ifname
 - transports

Also adds inference of link layer from transports or ifname.

At the moment these new fields are not populated in
ConnectivityMetricsEvent. Follow-up patches will fill this gap for
the events of the android.net.metrics package.

Test: new unit tests, $ runtest frameworks-net passes
Bug: 34901696
Change-Id: I563a6a3183470bdfaabb7c781a1beaf6b1058bf0
parent b096e588
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -423,8 +423,10 @@ public final class NetworkCapabilities implements Parcelable {
     */
    public static final int TRANSPORT_WIFI_AWARE = 5;

    private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
    private static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
    /** @hide */
    public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
    /** @hide */
    public static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;

    /**
     * Adds the given transport type to this {@code NetworkCapability} instance.
+84 −3
Original line number Diff line number Diff line
@@ -16,6 +16,17 @@

package com.android.server.connectivity;

import static android.net.NetworkCapabilities.MAX_TRANSPORT;
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_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;

import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
@@ -29,14 +40,12 @@ import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.os.Parcelable;
import android.util.SparseArray;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;

/** {@hide} */
final public class IpConnectivityEventBuilder {
@@ -73,6 +82,12 @@ final public class IpConnectivityEventBuilder {
            return null;
        }
        out.timeMs = ev.timestamp;
        out.networkId = ev.netId;
        out.transports = ev.transports;
        if (ev.ifname != null) {
          out.ifName = ev.ifname;
        }
        inferLinkLayer(out);
        return out;
    }

@@ -280,4 +295,70 @@ final public class IpConnectivityEventBuilder {
    private static boolean isBitSet(int flags, int bit) {
        return (flags & (1 << bit)) != 0;
    }

    private static void inferLinkLayer(IpConnectivityEvent ev) {
        int linkLayer = IpConnectivityLogClass.UNKNOWN;
        if (ev.transports != 0) {
            linkLayer = transportsToLinkLayer(ev.transports);
        } else if (ev.ifName != null) {
            linkLayer = ifnameToLinkLayer(ev.ifName);
        }
        if (linkLayer == IpConnectivityLogClass.UNKNOWN) {
            return;
        }
        ev.linkLayer = linkLayer;
        ev.ifName = "";
    }

    private static int transportsToLinkLayer(long transports) {
        switch (Long.bitCount(transports)) {
            case 0:
                return IpConnectivityLogClass.UNKNOWN;
            case 1:
                int t = Long.numberOfTrailingZeros(transports);
                return transportToLinkLayer(t);
            default:
                return IpConnectivityLogClass.MULTIPLE;
        }
    }

    private static int transportToLinkLayer(int transport) {
        if (0 <= transport && transport < TRANSPORT_LINKLAYER_MAP.length) {
            return TRANSPORT_LINKLAYER_MAP[transport];
        }
        return IpConnectivityLogClass.UNKNOWN;
    }

    private static final int[] TRANSPORT_LINKLAYER_MAP = new int[MAX_TRANSPORT + 1];
    static {
        TRANSPORT_LINKLAYER_MAP[TRANSPORT_CELLULAR]   = IpConnectivityLogClass.CELLULAR;
        TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI]       = IpConnectivityLogClass.WIFI;
        TRANSPORT_LINKLAYER_MAP[TRANSPORT_BLUETOOTH]  = IpConnectivityLogClass.BLUETOOTH;
        TRANSPORT_LINKLAYER_MAP[TRANSPORT_ETHERNET]   = IpConnectivityLogClass.ETHERNET;
        TRANSPORT_LINKLAYER_MAP[TRANSPORT_VPN]        = IpConnectivityLogClass.UNKNOWN;
        // TODO: change mapping TRANSPORT_WIFI_AWARE -> WIFI_AWARE
        TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.UNKNOWN;
    };

    private static int ifnameToLinkLayer(String ifname) {
        // Do not try to catch all interface names with regexes, instead only catch patterns that
        // are cheap to check, and otherwise fallback on postprocessing in aggregation layer.
        for (int i = 0; i < IFNAME_LINKLAYER_MAP.size(); i++) {
            String pattern = IFNAME_LINKLAYER_MAP.valueAt(i);
            if (ifname.startsWith(pattern)) {
                return IFNAME_LINKLAYER_MAP.keyAt(i);
            }
        }
        return IpConnectivityLogClass.UNKNOWN;
    }

    private static final SparseArray<String> IFNAME_LINKLAYER_MAP = new SparseArray<String>();
    static {
        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.CELLULAR, "rmnet");
        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.WIFI, "wlan");
        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.BLUETOOTH, "bt-pan");
        // TODO: rekey to USB
        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.ETHERNET, "usb");
        // TODO: add mappings for nan -> WIFI_AWARE and p2p -> WIFI_P2P
    }
}
+135 −0
Original line number Diff line number Diff line
@@ -26,6 +26,11 @@ import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
import static com.android.server.connectivity.MetricsTestUtil.b;
import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.BLUETOOTH;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.CELLULAR;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ETHERNET;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI;

import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
@@ -46,6 +51,136 @@ import junit.framework.TestCase;
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
public class IpConnectivityEventBuilderTest extends TestCase {

    @SmallTest
    public void testLinkLayerInferrence() {
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(IpReachabilityEvent.class),
                aString("wlan0"),
                anInt(IpReachabilityEvent.NUD_FAILED));

        String want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                "  link_layer: 0",
                "  network_id: 0",
                "  time_ms: 1",
                "  transports: 0",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);

        ev.netId = 123;
        ev.transports = 3; // transports have priority for inferrence of link layer
        ev.ifname = "wlan0";
        want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                String.format("  link_layer: %d", MULTIPLE),
                "  network_id: 123",
                "  time_ms: 1",
                "  transports: 3",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);

        ev.transports = 1;
        ev.ifname = null;
        want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                String.format("  link_layer: %d", CELLULAR),
                "  network_id: 123",
                "  time_ms: 1",
                "  transports: 1",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);

        ev.transports = 0;
        ev.ifname = "not_inferred";
        want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"not_inferred\"",
                "  link_layer: 0",
                "  network_id: 123",
                "  time_ms: 1",
                "  transports: 0",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);

        ev.ifname = "bt-pan";
        want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                String.format("  link_layer: %d", BLUETOOTH),
                "  network_id: 123",
                "  time_ms: 1",
                "  transports: 0",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);

        ev.ifname = "rmnet_ipa0";
        want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                String.format("  link_layer: %d", CELLULAR),
                "  network_id: 123",
                "  time_ms: 1",
                "  transports: 0",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);

        ev.ifname = "wlan0";
        want = joinLines(
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                String.format("  link_layer: %d", WIFI),
                "  network_id: 123",
                "  time_ms: 1",
                "  transports: 0",
                "  ip_reachability_event <",
                "    event_type: 512",
                "    if_name: \"wlan0\"",
                "  >",
                ">",
                "version: 2");
        verifySerialization(want, ev);
    }

    @SmallTest
    public void testDefaultNetworkEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(