diff options
| author | 2017-03-23 18:38:22 +0900 | |
|---|---|---|
| committer | 2017-04-14 01:44:52 +0000 | |
| commit | 5eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45 (patch) | |
| tree | 8a7df3645c6829e2f9371e8619f49cc346f271c5 | |
| parent | 2a5cfb9738867da634a3bf64ccef881aaefbfce5 (diff) | |
Connectivity metrics: add transports to connect stats
This patch groups connect() events per netId. It adds netid and
transport information to serialized ConnectStatistics events.
Test: updated NetdEventListenerServiceTest
updated IpConnectivityMetricsTest
$ runtest frameworks-net passes
Bug: 34901696
Change-Id: Id0d536ff723ded5c26eafe0bb138ba75ba2856c5
Merged-In: I4769496383943e714a1d350c298e093c2ed57477
(cherry picked from commit dfc2cc5857199345e08f07977b79b20292f964a2)
7 files changed, 373 insertions, 219 deletions
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java index 214edeeb5382..30b2656227d0 100644 --- a/core/java/android/net/metrics/ConnectStats.java +++ b/core/java/android/net/metrics/ConnectStats.java @@ -16,53 +16,47 @@ package android.net.metrics; +import android.net.NetworkCapabilities; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; +import com.android.internal.util.BitUtils; import com.android.internal.util.TokenBucket; -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics; -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair; /** - * A class that aggregates connect() statistics and helps build - * IpConnectivityLogClass.ConnectStatistics instances. - * + * A class that aggregates connect() statistics. * {@hide} */ public class ConnectStats { private final static int EALREADY = OsConstants.EALREADY; private final static int EINPROGRESS = OsConstants.EINPROGRESS; + /** Network id of the network associated with the event, or 0 if unspecified. */ + public final int netId; + /** Transports of the network associated with the event, as defined in NetworkCapabilities. */ + public final long transports; /** How many events resulted in a given errno. */ - private final SparseIntArray mErrnos = new SparseIntArray(); - /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */ - private final IntArray mLatencies = new IntArray(); + public final SparseIntArray errnos = new SparseIntArray(); + /** Latencies of successful blocking connects. TODO: add non-blocking connects latencies. */ + public final IntArray latencies = new IntArray(); /** TokenBucket for rate limiting latency recording. */ - private final TokenBucket mLatencyTb; + public final TokenBucket mLatencyTb; /** Maximum number of latency values recorded. */ - private final int mMaxLatencyRecords; + public final int mMaxLatencyRecords; /** Total count of successful connects. */ - private int mConnectCount = 0; + public int connectCount = 0; /** Total count of successful connects done in blocking mode. */ - private int mConnectBlockingCount = 0; + public int connectBlockingCount = 0; /** Total count of successful connects with IPv6 socket address. */ - private int mIpv6ConnectCount = 0; + public int ipv6ConnectCount = 0; - public ConnectStats(TokenBucket tb, int maxLatencyRecords) { + public ConnectStats(int netId, long transports, TokenBucket tb, int maxLatencyRecords) { + this.netId = netId; + this.transports = transports; mLatencyTb = tb; mMaxLatencyRecords = maxLatencyRecords; } - public ConnectStatistics toProto() { - ConnectStatistics stats = new ConnectStatistics(); - stats.connectCount = mConnectCount; - stats.connectBlockingCount = mConnectBlockingCount; - stats.ipv6AddrCount = mIpv6ConnectCount; - stats.latenciesMs = mLatencies.toArray(); - stats.errnosCounters = toPairArrays(mErrnos); - return stats; - } - public void addEvent(int errno, int latencyMs, String ipAddr) { if (isSuccess(errno)) { countConnect(errno, ipAddr); @@ -73,12 +67,12 @@ public class ConnectStats { } private void countConnect(int errno, String ipAddr) { - mConnectCount++; + connectCount++; if (!isNonBlocking(errno)) { - mConnectBlockingCount++; + connectBlockingCount++; } if (isIPv6(ipAddr)) { - mIpv6ConnectCount++; + ipv6ConnectCount++; } } @@ -91,16 +85,16 @@ public class ConnectStats { // Rate limited return; } - if (mLatencies.size() >= mMaxLatencyRecords) { + if (latencies.size() >= mMaxLatencyRecords) { // Hard limit the total number of latency measurements. return; } - mLatencies.add(ms); + latencies.add(ms); } private void countError(int errno) { - final int newcount = mErrnos.get(errno, 0) + 1; - mErrnos.put(errno, newcount); + final int newcount = errnos.get(errno, 0) + 1; + errnos.put(errno, newcount); } private static boolean isSuccess(int errno) { @@ -117,27 +111,18 @@ public class ConnectStats { return ipAddr.contains(":"); } - private static Pair[] toPairArrays(SparseIntArray counts) { - final int s = counts.size(); - Pair[] pairs = new Pair[s]; - for (int i = 0; i < s; i++) { - Pair p = new Pair(); - p.key = counts.keyAt(i); - p.value = counts.valueAt(i); - pairs[i] = p; - } - return pairs; - } - @Override public String toString() { - StringBuilder builder = new StringBuilder("ConnectStats(") - .append(String.format("%d success, ", mConnectCount)) - .append(String.format("%d blocking, ", mConnectBlockingCount)) - .append(String.format("%d IPv6 dst", mIpv6ConnectCount)); - for (int i = 0; i < mErrnos.size(); i++) { - String errno = OsConstants.errnoName(mErrnos.keyAt(i)); - int count = mErrnos.valueAt(i); + StringBuilder builder = new StringBuilder("ConnectStats(").append(netId).append(", "); + for (int t : BitUtils.unpackBits(transports)) { + builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); + } + builder.append(String.format("%d success, ", connectCount)); + builder.append(String.format("%d blocking, ", connectBlockingCount)); + builder.append(String.format("%d IPv6 dst", ipv6ConnectCount)); + for (int i = 0; i < errnos.size(); i++) { + String errno = OsConstants.errnoName(errnos.keyAt(i)); + int count = errnos.valueAt(i); builder.append(String.format(", %s: %d", errno, count)); } return builder.append(")").toString(); diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index dfa1ac13ea31..a4970e4d0d28 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -16,7 +16,9 @@ package android.net.metrics; +import android.net.NetworkCapabilities; import java.util.Arrays; +import com.android.internal.util.BitUtils; /** * A DNS event recorded by NetdEventListenerService. @@ -74,6 +76,10 @@ final public class DnsEvent { @Override public String toString() { - return String.format("DnsEvent(%d events)", eventCount); + StringBuilder builder = new StringBuilder("DnsEvent(").append(netId).append(", "); + for (int t : BitUtils.unpackBits(transports)) { + builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); + } + return builder.append(eventCount).append(" events)").toString(); } } diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java index f05acefadb3d..5dee91de0e77 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java @@ -23,9 +23,6 @@ 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; @@ -34,6 +31,7 @@ import android.net.metrics.DefaultNetworkEvent; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpErrorEvent; import android.net.metrics.DnsEvent; +import android.net.metrics.ConnectStats; import android.net.metrics.IpManagerEvent; import android.net.metrics.IpReachabilityEvent; import android.net.metrics.NetworkEvent; @@ -41,7 +39,12 @@ import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; import android.os.Parcelable; import android.util.SparseArray; +import android.util.SparseIntArray; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -77,35 +80,51 @@ final public class IpConnectivityEventBuilder { } public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) { - final IpConnectivityEvent out = new IpConnectivityEvent(); + final IpConnectivityEvent out = buildEvent(ev.netId, ev.transports, ev.ifname); + out.timeMs = ev.timestamp; if (!setEvent(out, ev.data)) { 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; } + public static IpConnectivityEvent toProto(ConnectStats in) { + IpConnectivityLogClass.ConnectStatistics stats = + new IpConnectivityLogClass.ConnectStatistics(); + stats.connectCount = in.connectCount; + stats.connectBlockingCount = in.connectBlockingCount; + stats.ipv6AddrCount = in.ipv6ConnectCount; + stats.latenciesMs = in.latencies.toArray(); + stats.errnosCounters = toPairArray(in.errnos); + final IpConnectivityEvent out = buildEvent(in.netId, in.transports, null); + out.setConnectStatistics(stats); + return out; + } + + public static IpConnectivityEvent toProto(DnsEvent in) { - final IpConnectivityEvent out = new IpConnectivityEvent(); IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch = new IpConnectivityLogClass.DNSLookupBatch(); in.resize(in.eventCount); dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes); dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes); dnsLookupBatch.latenciesMs = in.latenciesMs; + final IpConnectivityEvent out = buildEvent(in.netId, in.transports, null); out.setDnsLookupBatch(dnsLookupBatch); - out.networkId = in.netId; - out.transports = in.transports; - inferLinkLayer(out); return out; } + private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) { + final IpConnectivityEvent ev = new IpConnectivityEvent(); + ev.networkId = netId; + ev.transports = transports; + if (ifname != null) { + ev.ifName = ifname; + } + inferLinkLayer(ev); + return ev; + } + private static boolean setEvent(IpConnectivityEvent out, Parcelable in) { if (in instanceof DhcpErrorEvent) { setDhcpErrorEvent(out, (DhcpErrorEvent) in); @@ -268,6 +287,18 @@ final public class IpConnectivityEventBuilder { return out; } + private static Pair[] toPairArray(SparseIntArray counts) { + final int s = counts.size(); + Pair[] pairs = new Pair[s]; + for (int i = 0; i < s; i++) { + Pair p = new Pair(); + p.key = counts.keyAt(i); + p.value = counts.valueAt(i); + pairs[i] = p; + } + return pairs; + } + private static NetworkId netIdOf(int netid) { final NetworkId ni = new NetworkId(); ni.networkId = netid; diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 39ad584d4abf..214cfced8a3e 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -34,10 +34,11 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.BitUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.TokenBucket; -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.io.PrintWriter; import java.util.List; +import java.util.function.Function; +import java.util.function.IntFunction; /** * Implementation of the INetdEventListener interface. @@ -58,18 +59,17 @@ 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; - // Sparse array of DNS events, grouped by net id. + // 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<>(); private final ConnectivityManager mCm; @GuardedBy("this") private final TokenBucket mConnectTb = new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT); - @GuardedBy("this") - private ConnectStats mConnectStats = makeConnectStats(); - // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM // by the device owner. It's DevicePolicyManager's responsibility to ensure that. @GuardedBy("this") @@ -123,7 +123,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub { int port, int uid) throws RemoteException { maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs); - mConnectStats.addEvent(error, latencyMs, ipAddr); + ConnectStats connectStats = mConnectEvents.get(netId); + if (connectStats == null) { + connectStats = makeConnectStats(netId); + mConnectEvents.put(netId, connectStats); + } + connectStats.addEvent(error, latencyMs, ipAddr); if (mNetdEventCallback != null) { mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid); @@ -131,29 +136,8 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } public synchronized void flushStatistics(List<IpConnectivityEvent> events) { - flushConnectStats(events); - flushDnsStats(events); - } - - private static IpConnectivityEvent connectStatsProto(ConnectStats connectStats) { - // TODO: add transport information - IpConnectivityEvent ev = new IpConnectivityEvent(); - ev.setConnectStatistics(connectStats.toProto()); - return ev; - } - - private void flushConnectStats(List<IpConnectivityEvent> events) { - events.add(connectStatsProto(mConnectStats)); - mConnectStats = makeConnectStats(); - } - - private void flushDnsStats(List<IpConnectivityEvent> events) { - // TODO: migrate DnsEventBatch to IpConnectivityLogClass.DNSLatencies - for (int i = 0; i < mDnsEvents.size(); i++) { - IpConnectivityEvent ev = IpConnectivityEventBuilder.toProto(mDnsEvents.valueAt(i)); - events.add(ev); - } - mDnsEvents.clear(); + flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto); + flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto); } public synchronized void dump(PrintWriter writer) { @@ -165,22 +149,33 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } public synchronized void list(PrintWriter pw) { - for (int i = 0; i < mDnsEvents.size(); i++) { - pw.println(mDnsEvents.valueAt(i).toString()); - } - pw.println(mConnectStats.toString()); + listEvents(pw, mConnectEvents, (x) -> x); + listEvents(pw, mDnsEvents, (x) -> x); } public synchronized void listAsProtos(PrintWriter pw) { - for (int i = 0; i < mDnsEvents.size(); i++) { - IpConnectivityEvent ev = IpConnectivityEventBuilder.toProto(mDnsEvents.valueAt(i)); - pw.println(ev.toString()); + listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto); + listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto); + } + + private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in, + Function<T, IpConnectivityEvent> mapper) { + for (int i = 0; i < in.size(); i++) { + out.add(mapper.apply(in.valueAt(i))); } - pw.println(connectStatsProto(mConnectStats).toString()); + in.clear(); } - private ConnectStats makeConnectStats() { - return new ConnectStats(mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS); + public 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 ConnectStats makeConnectStats(int netId) { + long transports = getTransports(netId); + return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS); } private DnsEvent makeDnsEvent(int netId) { diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java index 66ca4b6b7eb5..d11565abb917 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java @@ -45,7 +45,9 @@ import android.net.metrics.NetworkEvent; import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; import android.test.suitebuilder.annotation.SmallTest; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.util.Arrays; +import java.util.List; import junit.framework.TestCase; // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. @@ -483,8 +485,9 @@ public class IpConnectivityEventBuilderTest extends TestCase { static void verifySerialization(String want, ConnectivityMetricsEvent... input) { try { - byte[] got = IpConnectivityEventBuilder.serialize(0, - IpConnectivityEventBuilder.toProto(Arrays.asList(input))); + List<IpConnectivityEvent> proto = + IpConnectivityEventBuilder.toProto(Arrays.asList(input)); + byte[] got = IpConnectivityEventBuilder.serialize(0, proto); IpConnectivityLog log = IpConnectivityLog.parseFrom(got); assertEquals(want, log.toString()); } catch (Exception e) { diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index fd81c90a050c..e01469b1825e 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -16,12 +16,22 @@ package com.android.server.connectivity; +import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; +import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; +import android.net.ConnectivityManager; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.metrics.ApfProgramEvent; import android.net.metrics.ApfStats; import android.net.metrics.DefaultNetworkEvent; @@ -31,7 +41,9 @@ import android.net.metrics.IpManagerEvent; import android.net.metrics.IpReachabilityEvent; import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; +import android.system.OsConstants; import android.os.Parcelable; +import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.util.Base64; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; @@ -41,26 +53,38 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import junit.framework.TestCase; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; -public class IpConnectivityMetricsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpConnectivityMetricsTest { static final IpReachabilityEvent FAKE_EV = new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); + private static final String EXAMPLE_IPV4 = "192.0.2.1"; + private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; + @Mock Context mCtx; @Mock IIpConnectivityMetrics mMockService; + @Mock ConnectivityManager mCm; IpConnectivityMetrics mService; + NetdEventListenerService mNetdListener; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000); + mNetdListener = new NetdEventListenerService(mCm); + mService.mNetdListener = mNetdListener; } - @SmallTest + @Test public void testLoggingEvents() throws Exception { IpConnectivityLog logger = new IpConnectivityLog(mMockService); @@ -74,7 +98,7 @@ public class IpConnectivityMetricsTest extends TestCase { assertEventsEqual(expectedEvent(3), got.get(2)); } - @SmallTest + @Test public void testLoggingEventsWithMultipleCallers() throws Exception { IpConnectivityLog logger = new IpConnectivityLog(mMockService); @@ -91,7 +115,7 @@ public class IpConnectivityMetricsTest extends TestCase { }.start(); } - List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 100); + List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200); Collections.sort(got, EVENT_COMPARATOR); Iterator<ConnectivityMetricsEvent> iter = got.iterator(); for (int i = 0; i < nCallers; i++) { @@ -102,7 +126,7 @@ public class IpConnectivityMetricsTest extends TestCase { } } - @SmallTest + @Test public void testBufferFlushing() { String output1 = getdump("flush"); assertEquals("", output1); @@ -115,7 +139,7 @@ public class IpConnectivityMetricsTest extends TestCase { assertEquals("", output3); } - @SmallTest + @Test public void testRateLimiting() { final IpConnectivityLog logger = new IpConnectivityLog(mService.impl); final ApfProgramEvent ev = new ApfProgramEvent(); @@ -137,12 +161,19 @@ public class IpConnectivityMetricsTest extends TestCase { assertEquals("", output2); } - @SmallTest - // TODO: add NetdEventListenerService coverage too - public void testEndToEndLogging() { + @Test + public void testEndToEndLogging() throws Exception { // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. IpConnectivityLog logger = new IpConnectivityLog(mService.impl); + NetworkCapabilities ncWifi = new NetworkCapabilities(); + NetworkCapabilities ncCell = new NetworkCapabilities(); + ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + + when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); + when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); + ApfStats apfStats = new ApfStats(); apfStats.durationMs = 45000; apfStats.receivedRas = 10; @@ -178,6 +209,21 @@ public class IpConnectivityMetricsTest extends TestCase { logger.log(ev); } + // netId, errno, latency, destination + connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4); + connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6); + connectEvent(100, 0, 110, EXAMPLE_IPV4); + connectEvent(101, 0, 23, EXAMPLE_IPV4); + connectEvent(101, 0, 45, EXAMPLE_IPV6); + connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4); + + // netId, type, return code, latency + dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); + dnsEvent(100, EVENT_GETADDRINFO, 3, 45); + dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638); + dnsEvent(101, EVENT_GETADDRINFO, 0, 56); + dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34); + String want = String.join("\n", "dropped_events: 0", "events <", @@ -280,6 +326,70 @@ public class IpConnectivityMetricsTest extends TestCase { " router_lifetime: 2000", " >", ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " connect_statistics <", + " connect_blocking_count: 1", + " connect_count: 3", + " errnos_counters <", + " key: 11", + " value: 1", + " >", + " ipv6_addr_count: 1", + " latencies_ms: 110", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " connect_statistics <", + " connect_blocking_count: 2", + " connect_count: 2", + " ipv6_addr_count: 1", + " latencies_ms: 23", + " latencies_ms: 45", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 1", + " event_types: 2", + " latencies_ms: 3456", + " latencies_ms: 45", + " latencies_ms: 638", + " return_codes: 0", + " return_codes: 3", + " return_codes: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 2", + " latencies_ms: 56", + " latencies_ms: 34", + " return_codes: 0", + " return_codes: 0", + " >", + ">", "version: 2\n"); verifySerialization(want, getdump("flush")); @@ -292,6 +402,14 @@ public class IpConnectivityMetricsTest extends TestCase { return buffer.toString(); } + void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception { + mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1); + } + + void dnsEvent(int netId, int type, int result, int latency) throws Exception { + mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); + } + List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception { ArgumentCaptor<ConnectivityMetricsEvent> captor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java index 05c976732505..f98ab3d06921 100644 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -62,20 +62,22 @@ public class NetdEventListenerServiceTest { @Before public void setUp() { - mCm = mock(ConnectivityManager.class); - mNetdEventListenerService = new NetdEventListenerService(mCm); - } - - @Test - public void testDnsLogging() throws Exception { NetworkCapabilities ncWifi = new NetworkCapabilities(); NetworkCapabilities ncCell = new NetworkCapabilities(); ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + mCm = mock(ConnectivityManager.class); when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); + mNetdEventListenerService = new NetdEventListenerService(mCm); + } + + @Test + public void testDnsLogging() throws Exception { + asyncDump(100); + dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); dnsEvent(100, EVENT_GETADDRINFO, 0, 267); dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230); @@ -96,18 +98,6 @@ public class NetdEventListenerServiceTest { "dropped_events: 0", "events <", " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " connect_statistics <", - " connect_blocking_count: 0", - " connect_count: 0", - " ipv6_addr_count: 0", - " >", - ">", - "events <", - " if_name: \"\"", " link_layer: 4", " network_id: 100", " time_ms: 0", @@ -172,34 +162,36 @@ public class NetdEventListenerServiceTest { @Test public void testConnectLogging() throws Exception { + asyncDump(100); + final int OK = 0; Thread[] logActions = { // ignored - connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), + connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), // valid latencies - connectEventAction(OK, 110, EXAMPLE_IPV4), - connectEventAction(OK, 23, EXAMPLE_IPV4), - connectEventAction(OK, 45, EXAMPLE_IPV4), - connectEventAction(OK, 56, EXAMPLE_IPV4), - connectEventAction(OK, 523, EXAMPLE_IPV6), - connectEventAction(OK, 214, EXAMPLE_IPV6), - connectEventAction(OK, 67, EXAMPLE_IPV6), + connectEventAction(100, OK, 110, EXAMPLE_IPV4), + connectEventAction(100, OK, 23, EXAMPLE_IPV4), + connectEventAction(100, OK, 45, EXAMPLE_IPV4), + connectEventAction(101, OK, 56, EXAMPLE_IPV4), + connectEventAction(101, OK, 523, EXAMPLE_IPV6), + connectEventAction(101, OK, 214, EXAMPLE_IPV6), + connectEventAction(101, OK, 67, EXAMPLE_IPV6), // errors - connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), + connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4), }; for (Thread t : logActions) { @@ -209,59 +201,84 @@ public class NetdEventListenerServiceTest { t.join(); } - List<IpConnectivityEvent> events = new ArrayList<>(); - mNetdEventListenerService.flushStatistics(events); - - IpConnectivityEvent got = events.get(0); + String got = flushStatistics(); String want = String.join("\n", - "if_name: \"\"", - "link_layer: 0", - "network_id: 0", - "time_ms: 0", - "transports: 0", - "connect_statistics <", - " connect_blocking_count: 7", - " connect_count: 12", - " errnos_counters <", - " key: 1", - " value: 2", - " >", - " errnos_counters <", - " key: 11", - " value: 1", - " >", - " errnos_counters <", - " key: 13", - " value: 3", - " >", - " errnos_counters <", - " key: 98", - " value: 1", - " >", - " errnos_counters <", - " key: 110", - " value: 3", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " connect_statistics <", + " connect_blocking_count: 3", + " connect_count: 6", + " errnos_counters <", + " key: 1", + " value: 1", + " >", + " errnos_counters <", + " key: 11", + " value: 1", + " >", + " errnos_counters <", + " key: 13", + " value: 1", + " >", + " errnos_counters <", + " key: 98", + " value: 1", + " >", + " errnos_counters <", + " key: 110", + " value: 2", + " >", + " ipv6_addr_count: 1", + " latencies_ms: 23", + " latencies_ms: 45", + " latencies_ms: 110", " >", - " errnos_counters <", - " key: 111", - " value: 1", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " connect_statistics <", + " connect_blocking_count: 4", + " connect_count: 6", + " errnos_counters <", + " key: 1", + " value: 1", + " >", + " errnos_counters <", + " key: 13", + " value: 2", + " >", + " errnos_counters <", + " key: 110", + " value: 1", + " >", + " errnos_counters <", + " key: 111", + " value: 1", + " >", + " ipv6_addr_count: 5", + " latencies_ms: 56", + " latencies_ms: 67", + " latencies_ms: 214", + " latencies_ms: 523", " >", - " ipv6_addr_count: 6", - " latencies_ms: 23", - " latencies_ms: 45", - " latencies_ms: 56", - " latencies_ms: 67", - " latencies_ms: 110", - " latencies_ms: 214", - " latencies_ms: 523", - ">\n"); - verifyConnectEvent(want, got); + ">", + "version: 2\n"); + assertEquals(want, got); } - Thread connectEventAction(int error, int latencyMs, String ipAddr) { + Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) { return new Thread(() -> { try { - mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1); + mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); } catch (Exception e) { fail(e.toString()); } @@ -272,25 +289,14 @@ public class NetdEventListenerServiceTest { mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0); } - Thread dumpAction(long durationMs) throws Exception { + void asyncDump(long durationMs) throws Exception { final long stop = System.currentTimeMillis() + durationMs; final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null")); - return new Thread(() -> { + new Thread(() -> { while (System.currentTimeMillis() < stop) { mNetdEventListenerService.dump(pw); } - }); - } - - static void verifyConnectEvent(String expected, IpConnectivityEvent got) { - try { - Arrays.sort(got.getConnectStatistics().latenciesMs); - Arrays.sort(got.getConnectStatistics().errnosCounters, - Comparator.comparingInt((p) -> p.key)); - assertEquals(expected, got.toString()); - } catch (Exception e) { - fail(e.toString()); - } + }).start(); } // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. @@ -303,6 +309,16 @@ public class NetdEventListenerServiceTest { PrintWriter writer = new PrintWriter(buffer); metricsService.impl.dump(null, writer, new String[]{"flush"}); byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT); - return IpConnectivityLog.parseFrom(bytes).toString(); + IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes); + for (IpConnectivityEvent ev : log.events) { + if (ev.getConnectStatistics() == null) { + continue; + } + // Sort repeated fields of connect() events arriving in non-deterministic order. + Arrays.sort(ev.getConnectStatistics().latenciesMs); + Arrays.sort(ev.getConnectStatistics().errnosCounters, + Comparator.comparingInt((p) -> p.key)); + } + return log.toString(); } } |