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)
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 66ca4b6..d11565a 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.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 @@
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 fd81c90..e01469b 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.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.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 @@
assertEventsEqual(expectedEvent(3), got.get(2));
}
- @SmallTest
+ @Test
public void testLoggingEventsWithMultipleCallers() throws Exception {
IpConnectivityLog logger = new IpConnectivityLog(mMockService);
@@ -91,7 +115,7 @@
}.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 @@
}
}
- @SmallTest
+ @Test
public void testBufferFlushing() {
String output1 = getdump("flush");
assertEquals("", output1);
@@ -115,7 +139,7 @@
assertEquals("", output3);
}
- @SmallTest
+ @Test
public void testRateLimiting() {
final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
final ApfProgramEvent ev = new ApfProgramEvent();
@@ -137,12 +161,19 @@
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 @@
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 @@
" 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 @@
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 05c9767..f98ab3d 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 @@
@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 @@
"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 @@
@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 @@
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",
+ "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: 11",
- " 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",
" >",
- " errnos_counters <",
- " key: 13",
- " value: 3",
- " >",
- " errnos_counters <",
- " key: 98",
- " value: 1",
- " >",
- " errnos_counters <",
- " key: 110",
- " value: 3",
- " >",
- " errnos_counters <",
- " key: 111",
- " value: 1",
- " >",
- " 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 @@
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 @@
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();
}
}