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

Commit a87b15c9 authored by Hugo Benichi's avatar Hugo Benichi
Browse files

Connectivity metrics: add WakeupStats events

This patch defines a new WakeupStats event in ipconnectivity.proto and
populates these events from the NFLOG wakeup events stored in
NetdEventListenerService.

There is one WakeupStats object per known interface on which ingress
packets arrive and may wake the system up.

Example from $ adb shell dumpsys connmetrics list:

...
WakeupStats(wlan0, total: 58, root: 0, system: 3, apps: 38, non-apps: 0, unrouted: 17, 6111s)
WakeupEvent(13:36:31.686, iface wlan0, uid -1)
WakeupEvent(13:38:50.846, iface wlan0, uid -1)
WakeupEvent(13:39:16.676, iface wlan0, uid 10065)
WakeupEvent(13:40:32.144, iface wlan0, uid 1000)
WakeupEvent(13:40:35.827, iface wlan0, uid 1000)
WakeupEvent(13:40:47.913, iface wlan0, uid 10004)
WakeupEvent(13:40:52.622, iface wlan0, uid 10014)
WakeupEvent(13:41:06.036, iface wlan0, uid 10004)
...

Bug: 34901696
Bug: 62179647
Test: runtest frameworks-net
Merged-In: Ie2676b20bfb411a1902f4942643df0c20e268d99

(cherry pick from commit 60c9f63b)

Change-Id: I3087f446fc998fc1ca895d975b80c4a1dd029bf3
parent 1e327eda
Loading
Loading
Loading
Loading
+87 −0
Original line number Original line 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;

import android.os.Process;
import android.os.SystemClock;

/**
 * An event logged per interface and that aggregates WakeupEvents for that interface.
 * {@hide}
 */
public class WakeupStats {

    private static final int NO_UID = -1;

    public final long creationTimeMs = SystemClock.elapsedRealtime();
    public final String iface;

    public long totalWakeups = 0;
    public long rootWakeups = 0;
    public long systemWakeups = 0;
    public long nonApplicationWakeups = 0;
    public long applicationWakeups = 0;
    public long unroutedWakeups = 0;
    public long durationSec = 0;

    public WakeupStats(String iface) {
        this.iface = iface;
    }

    /** Update durationSec with current time. */
    public void updateDuration() {
        durationSec = (SystemClock.elapsedRealtime() - creationTimeMs) / 1000;
    }

    /** Update wakeup counters for the given WakeupEvent. */
    public void countEvent(WakeupEvent ev) {
        totalWakeups++;
        switch (ev.uid) {
            case Process.ROOT_UID:
                rootWakeups++;
                break;
            case Process.SYSTEM_UID:
                systemWakeups++;
                break;
            case NO_UID:
                unroutedWakeups++;
                break;
            default:
                if (ev.uid >= Process.FIRST_APPLICATION_UID) {
                    applicationWakeups++;
                } else {
                    nonApplicationWakeups++;
                }
                break;
        }
    }

    @Override
    public String toString() {
        updateDuration();
        return new StringBuilder()
                .append("WakeupStats(").append(iface)
                .append(", total: ").append(totalWakeups)
                .append(", root: ").append(rootWakeups)
                .append(", system: ").append(systemWakeups)
                .append(", apps: ").append(applicationWakeups)
                .append(", non-apps: ").append(nonApplicationWakeups)
                .append(", unrouted: ").append(unroutedWakeups)
                .append(", ").append(durationSec).append("s)")
                .toString();
    }
}
+35 −0
Original line number Original line Diff line number Diff line
@@ -473,6 +473,38 @@ message NetworkStats {
  repeated Pair validation_states = 8;
  repeated Pair validation_states = 8;
}
}


// Represents statistics from NFLOG wakeup events due to ingress packets.
// Since oc-mr1.
// Next tag: 8.
message WakeupStats {
  // The time duration in seconds covered by these stats, for deriving
  // exact wakeup rates.
  optional int64 duration_sec = 1;

  // The total number of ingress packets waking up the device.
  optional int64 total_wakeups = 2;

  // The total number of wakeup packets routed to a socket belonging to
  // the root uid (uid 0).
  optional int64 root_wakeups = 3;

  // The total number of wakeup packets routed to a socket belonging to
  // the system server (uid 1000).
  optional int64 system_wakeups = 4;

  // The total number of wakeup packets routed to a socket belonging to
  // an application (uid > 9999).
  optional int64 application_wakeups = 5;

  // The total number of wakeup packets routed to a socket belonging to another
  // uid than the root uid, system uid, or an application uid (any uid in
  // between [1001, 9999]. See android.os.Process for possible uids.
  optional int64 non_application_wakeups = 6;

  // The total number of wakeup packets with no associated sockets.
  optional int64 unrouted_wakeups = 7;
}

// Represents one of the IP connectivity event defined in this file.
// Represents one of the IP connectivity event defined in this file.
// Next tag: 20
// Next tag: 20
message IpConnectivityEvent {
message IpConnectivityEvent {
@@ -547,6 +579,9 @@ message IpConnectivityEvent {


    // Network statistics.
    // Network statistics.
    NetworkStats network_stats = 19;
    NetworkStats network_stats = 19;

    // Ingress packet wakeup statistics.
    WakeupStats wakeup_stats = 20;
  };
  };
};
};


+17 −0
Original line number Original line Diff line number Diff line
@@ -38,6 +38,7 @@ import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.metrics.WakeupStats;
import android.os.Parcelable;
import android.os.Parcelable;
import android.util.SparseArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseIntArray;
@@ -115,6 +116,22 @@ final public class IpConnectivityEventBuilder {
        return out;
        return out;
    }
    }


    public static IpConnectivityEvent toProto(WakeupStats in) {
        IpConnectivityLogClass.WakeupStats wakeupStats =
                new IpConnectivityLogClass.WakeupStats();
        in.updateDuration();
        wakeupStats.durationSec = in.durationSec;
        wakeupStats.totalWakeups = in.totalWakeups;
        wakeupStats.rootWakeups = in.rootWakeups;
        wakeupStats.systemWakeups = in.systemWakeups;
        wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
        wakeupStats.applicationWakeups = in.applicationWakeups;
        wakeupStats.unroutedWakeups = in.unroutedWakeups;
        final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
        out.setWakeupStats(wakeupStats);
        return out;
    }

    private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
    private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
        final IpConnectivityEvent ev = new IpConnectivityEvent();
        final IpConnectivityEvent ev = new IpConnectivityEvent();
        ev.networkId = netId;
        ev.networkId = netId;
+45 −18
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.server.connectivity;
package com.android.server.connectivity;


import static android.util.TimeUtils.NANOS_PER_MS;

import android.content.Context;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager;
import android.net.INetdEventCallback;
import android.net.INetdEventCallback;
@@ -26,9 +28,11 @@ import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.WakeupEvent;
import android.net.metrics.WakeupEvent;
import android.net.metrics.WakeupStats;
import android.os.RemoteException;
import android.os.RemoteException;
import android.text.format.DateUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Log;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
@@ -62,8 +66,10 @@ public class NetdEventListenerService extends INetdEventListener.Stub {


    @VisibleForTesting
    @VisibleForTesting
    static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
    static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;

    // TODO: dedup this String constant with the one used in
    private static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
    // ConnectivityService#wakeupModifyInterface().
    @VisibleForTesting
    static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";


    // Sparse arrays of DNS and connect events, grouped by net id.
    // Sparse arrays of DNS and connect events, grouped by net id.
    @GuardedBy("this")
    @GuardedBy("this")
@@ -71,6 +77,9 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
    @GuardedBy("this")
    @GuardedBy("this")
    private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
    private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();


    // Array of aggregated wakeup event stats, grouped by interface name.
    @GuardedBy("this")
    private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
    // Ring buffer array for storing packet wake up events sent by Netd.
    // Ring buffer array for storing packet wake up events sent by Netd.
    @GuardedBy("this")
    @GuardedBy("this")
    private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
    private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
@@ -154,9 +163,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
        // TODO: add ip protocol and port
        // TODO: add ip protocol and port


        String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
        String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
        long timestampMs = timestampNs / 1000000;
        final long timestampMs;
        // FIXME: Netd timestampNs is always 0.
        if (timestampNs > 0) {
            timestampMs = timestampNs / NANOS_PER_MS;
        } else {
            timestampMs = System.currentTimeMillis();
            timestampMs = System.currentTimeMillis();
        }


        addWakupEvent(iface, timestampMs, uid);
        addWakupEvent(iface, timestampMs, uid);
    }
    }
@@ -170,6 +182,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
        event.timestampMs = timestampMs;
        event.timestampMs = timestampMs;
        event.uid = uid;
        event.uid = uid;
        mWakeupEvents[index] = event;
        mWakeupEvents[index] = event;
        WakeupStats stats = mWakeupStats.get(iface);
        if (stats == null) {
            stats = new WakeupStats(iface);
            mWakeupStats.put(iface, stats);
        }
        stats.countEvent(event);
    }
    }


    @GuardedBy("this")
    @GuardedBy("this")
@@ -192,6 +210,10 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
    public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
    public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
        flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
        flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
        flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
        flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
        for (int i = 0; i < mWakeupStats.size(); i++) {
            events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
        }
        mWakeupStats.clear();
    }
    }


    public synchronized void dump(PrintWriter writer) {
    public synchronized void dump(PrintWriter writer) {
@@ -203,14 +225,22 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
    }
    }


    public synchronized void list(PrintWriter pw) {
    public synchronized void list(PrintWriter pw) {
        listEvents(pw, mConnectEvents, (x) -> x);
        listEvents(pw, mConnectEvents, (x) -> x, "\n");
        listEvents(pw, mDnsEvents, (x) -> x);
        listEvents(pw, mDnsEvents, (x) -> x, "\n");
        listWakeupEvents(pw, getWakeupEvents());
        for (int i = 0; i < mWakeupStats.size(); i++) {
            pw.println(mWakeupStats.valueAt(i));
        }
        for (WakeupEvent wakeup : getWakeupEvents()) {
            pw.println(wakeup);
        }
    }
    }


    public synchronized void listAsProtos(PrintWriter pw) {
    public synchronized void listAsProtos(PrintWriter pw) {
        listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto);
        listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, "");
        listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto);
        listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, "");
        for (int i = 0; i < mWakeupStats.size(); i++) {
            pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
        }
    }
    }


    private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
    private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
@@ -222,15 +252,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
    }
    }


    private static <T> void listEvents(
    private static <T> void listEvents(
            PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
            PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) {
        // Proto derived Classes have toString method that adds a \n at the end.
        // Let the caller control that by passing in the line separator explicitly.
        for (int i = 0; i < events.size(); i++) {
        for (int i = 0; i < events.size(); i++) {
            pw.println(mapper.apply(events.valueAt(i)).toString());
            pw.print(mapper.apply(events.valueAt(i)));
        }
            pw.print(separator);
    }

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


+68 −16
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.server.connectivity;
package com.android.server.connectivity;


import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static com.android.server.connectivity.MetricsTestUtil.aBool;
import static com.android.server.connectivity.MetricsTestUtil.aBool;
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
import static com.android.server.connectivity.MetricsTestUtil.aLong;
import static com.android.server.connectivity.MetricsTestUtil.aLong;
@@ -31,29 +33,41 @@ import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClas
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ETHERNET;
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.MULTIPLE;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;


import android.net.ConnectivityMetricsEvent;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.ApfStats;
import android.net.metrics.ConnectStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DnsEvent;
import android.net.metrics.DnsEvent;
import android.net.metrics.DnsEvent;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.metrics.WakeupStats;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;

import java.util.Arrays;
import java.util.Arrays;
import java.util.List;
import java.util.List;
import junit.framework.TestCase;


// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
import org.junit.runner.RunWith;
public class IpConnectivityEventBuilderTest extends TestCase {
import org.junit.Test;


// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
@RunWith(AndroidJUnit4.class)
@SmallTest
@SmallTest
public class IpConnectivityEventBuilderTest {

    @Test
    public void testLinkLayerInferrence() {
    public void testLinkLayerInferrence() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(IpReachabilityEvent.class),
                aType(IpReachabilityEvent.class),
@@ -182,7 +196,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testDefaultNetworkEventSerialization() {
    public void testDefaultNetworkEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(DefaultNetworkEvent.class),
                aType(DefaultNetworkEvent.class),
@@ -223,7 +237,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testDhcpClientEventSerialization() {
    public void testDhcpClientEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(DhcpClientEvent.class),
                aType(DhcpClientEvent.class),
@@ -249,7 +263,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testDhcpErrorEventSerialization() {
    public void testDhcpErrorEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(DhcpErrorEvent.class),
                aType(DhcpErrorEvent.class),
@@ -274,7 +288,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testIpManagerEventSerialization() {
    public void testIpManagerEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(IpManagerEvent.class),
                aType(IpManagerEvent.class),
@@ -300,7 +314,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testIpReachabilityEventSerialization() {
    public void testIpReachabilityEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(IpReachabilityEvent.class),
                aType(IpReachabilityEvent.class),
@@ -324,7 +338,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testNetworkEventSerialization() {
    public void testNetworkEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(NetworkEvent.class),
                aType(NetworkEvent.class),
@@ -353,7 +367,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testValidationProbeEventSerialization() {
    public void testValidationProbeEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(ValidationProbeEvent.class),
                aType(ValidationProbeEvent.class),
@@ -380,7 +394,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testApfProgramEventSerialization() {
    public void testApfProgramEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(ApfProgramEvent.class),
                aType(ApfProgramEvent.class),
@@ -414,7 +428,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testApfStatsSerialization() {
    public void testApfStatsSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(ApfStats.class),
                aType(ApfStats.class),
@@ -457,7 +471,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @SmallTest
    @Test
    public void testRaEventSerialization() {
    public void testRaEventSerialization() {
        ConnectivityMetricsEvent ev = describeIpEvent(
        ConnectivityMetricsEvent ev = describeIpEvent(
                aType(RaEvent.class),
                aType(RaEvent.class),
@@ -490,11 +504,49 @@ public class IpConnectivityEventBuilderTest extends TestCase {
        verifySerialization(want, ev);
        verifySerialization(want, ev);
    }
    }


    @Test
    public void testWakeupStatsSerialization() {
        WakeupStats stats = new WakeupStats("wlan0");
        stats.totalWakeups = 14;
        stats.applicationWakeups = 5;
        stats.nonApplicationWakeups = 1;
        stats.rootWakeups = 2;
        stats.systemWakeups = 3;
        stats.unroutedWakeups = 3;

        IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
        String want = String.join("\n",
                "dropped_events: 0",
                "events <",
                "  if_name: \"\"",
                "  link_layer: 4",
                "  network_id: 0",
                "  time_ms: 0",
                "  transports: 0",
                "  wakeup_stats <",
                "    application_wakeups: 5",
                "    duration_sec: 0",
                "    non_application_wakeups: 1",
                "    root_wakeups: 2",
                "    system_wakeups: 3",
                "    total_wakeups: 14",
                "    unrouted_wakeups: 3",
                "  >",
                ">",
                "version: 2\n");

        verifySerialization(want, got);
    }

    static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
    static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
        try {
        List<IpConnectivityEvent> protoInput =
            List<IpConnectivityEvent> proto =
                IpConnectivityEventBuilder.toProto(Arrays.asList(input));
                IpConnectivityEventBuilder.toProto(Arrays.asList(input));
            byte[] got = IpConnectivityEventBuilder.serialize(0, proto);
        verifySerialization(want, protoInput.toArray(new IpConnectivityEvent[0]));
    }

    static void verifySerialization(String want, IpConnectivityEvent... input) {
        try {
            byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
            IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
            IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
            assertEquals(want, log.toString());
            assertEquals(want, log.toString());
        } catch (Exception e) {
        } catch (Exception e) {
Loading