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

Commit 1e327eda authored by Hugo Benichi's avatar Hugo Benichi
Browse files

Connectivity metrics: collect NFLOG wakeup events

This patch stores NFLOG packet wakeup events sent by Netd to the system
server into a ring buffer inside NetdEventListenerService. The content
of this buffer is accessible by $ dumpsys connmetrics or $ dumpsys
connmetrics list, and is added to bug reports.

The wakeup event buffer stores currently uid and timestamps.

Bug: 34901696
Bug: 62179647
Test: runtest frameworks-net, new unit tests

Merged-In: Ie8db6f8572b1a929a20398d8dc03e189bc488382

(cherry picked from commit f562ac34)

Change-Id: Ibe706872a80dfd06abd9779a2116ca7e4bc0fb77
parent 8f50f52e
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 android.net.metrics;

/**
 * An event logged when NFLOG notifies userspace of a wakeup packet for
 * watched interfaces.
 * {@hide}
 */
public class WakeupEvent {
    public String iface;
    public long timestampMs;
    public int uid;

    @Override
    public String toString() {
        return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
                timestampMs, timestampMs, iface, uid);
    }
}
+58 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.net.metrics.ConnectStats;
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.WakeupEvent;
import android.os.RemoteException;
import android.text.format.DateUtils;
import android.util.Log;
@@ -59,12 +60,23 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
    private static final int CONNECT_LATENCY_FILL_RATE    = 15 * (int) DateUtils.SECOND_IN_MILLIS;
    private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;

    @VisibleForTesting
    static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;

    private static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";

    // Sparse arrays of DNS and connect events, grouped by net id.
    @GuardedBy("this")
    private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
    @GuardedBy("this")
    private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();

    // Ring buffer array for storing packet wake up events sent by Netd.
    @GuardedBy("this")
    private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
    @GuardedBy("this")
    private long mWakeupEventCursor = 0;

    private final ConnectivityManager mCm;

    @GuardedBy("this")
@@ -137,6 +149,44 @@ public class NetdEventListenerService extends INetdEventListener.Stub {

    @Override
    public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
        maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);

        // TODO: add ip protocol and port

        String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
        long timestampMs = timestampNs / 1000000;
        // FIXME: Netd timestampNs is always 0.
        timestampMs = System.currentTimeMillis();

        addWakupEvent(iface, timestampMs, uid);
    }

    @GuardedBy("this")
    private void addWakupEvent(String iface, long timestampMs, int uid) {
        int index = wakeupEventIndex(mWakeupEventCursor);
        mWakeupEventCursor++;
        WakeupEvent event = new WakeupEvent();
        event.iface = iface;
        event.timestampMs = timestampMs;
        event.uid = uid;
        mWakeupEvents[index] = event;
    }

    @GuardedBy("this")
    private WakeupEvent[] getWakeupEvents() {
        int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
        WakeupEvent[] out = new WakeupEvent[length];
        // Reverse iteration from youngest event to oldest event.
        long inCursor = mWakeupEventCursor - 1;
        int outIdx = out.length - 1;
        while (outIdx >= 0) {
            out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
        }
        return out;
    }

    private static int wakeupEventIndex(long cursor) {
        return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
    }

    public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
@@ -155,6 +205,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
    public synchronized void list(PrintWriter pw) {
        listEvents(pw, mConnectEvents, (x) -> x);
        listEvents(pw, mDnsEvents, (x) -> x);
        listWakeupEvents(pw, getWakeupEvents());
    }

    public synchronized void listAsProtos(PrintWriter pw) {
@@ -170,13 +221,19 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
        in.clear();
    }

    public static <T> void listEvents(
    private static <T> void listEvents(
            PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
        for (int i = 0; i < events.size(); i++) {
            pw.println(mapper.apply(events.valueAt(i)).toString());
        }
    }

    private static void listWakeupEvents(PrintWriter pw, WakeupEvent[] events) {
        for (WakeupEvent wakeup : events) {
            pw.println(wakeup);
        }
    }

    private ConnectStats makeConnectStats(int netId) {
        long transports = getTransports(netId);
        return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
+58 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -74,6 +75,52 @@ public class NetdEventListenerServiceTest {
        mNetdEventListenerService = new NetdEventListenerService(mCm);
    }

    @Test
    public void testWakeupEventLogging() throws Exception {
        final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;

        // Assert no events
        String[] events1 = listNetdEvent();
        assertEquals(new String[]{""}, events1);

        long now = System.currentTimeMillis();
        String prefix = "iface:wlan0";
        int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
        for (int uid : uids) {
            mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
        }

        String[] events2 = listNetdEvent();
        assertEquals(uids.length, events2.length);
        for (int i = 0; i < uids.length; i++) {
            String got = events2[i];
            assertContains(got, "wlan0");
            assertContains(got, "uid: " + uids[i]);
        }

        int uid = 20000;
        for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
            long ts = now + 10;
            mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
        }

        // Assert there are BUFFER_LENGTH events all with uid 20000
        String[] events3 = listNetdEvent();
        assertEquals(BUFFER_LENGTH, events3.length);
        for (String got : events3) {
            assertContains(got, "wlan0");
            assertContains(got, "uid: " + uid);
        }

        uid = 45678;
        mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);

        String[] events4 = listNetdEvent();
        String lastEvent = events4[events4.length - 1];
        assertContains(lastEvent, "wlan0");
        assertContains(lastEvent, "uid: " + uid);
    }

    @Test
    public void testDnsLogging() throws Exception {
        asyncDump(100);
@@ -329,4 +376,15 @@ public class NetdEventListenerServiceTest {
        }
        return log.toString();
    }

    String[] listNetdEvent() throws Exception {
        StringWriter buffer = new StringWriter();
        PrintWriter writer = new PrintWriter(buffer);
        mNetdEventListenerService.list(writer);
        return buffer.toString().split("\\n");
    }

    static void assertContains(String got, String want) {
        assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
    }
}