Connectivity metrics: log DnsEvents in-band
This patch changes how DnsEvents are logged in IpConnectivityMetrics.
The following changes are made:
- DnsEventBatch are not logged after 100 queries on the same network
- this allows to merge DnsEvent and DnsEventBatch into one class
- DnsEventBatch are not logged after a network disconnect
- this allows to remove the NetworkCallback
- DnsEvent are now logged similarly to ConnectStats when statistics are
flushed, in a direct call from IpConnectivityMetrics into
NetdEventListenerService, in a direct call from IpConnectivityMetrics
into NetdEventListenerService.
- this allows to remove the Parcelable implementation of DnsEvent
- transports information is added to DnsEvent.
Test: - simplified NetdEventListenerServiceTest covering dns logging
- updated IpConnectivityEventBuilderTest
- updated IpConnectivityMetricsTest
- $ runtest frameworks-net passes
- manually verified $ adb shell dumpsys connmetrics list proto
Bug: 34901696
Change-Id: I4fcd0ad7a7b85d587647f471a90c1e53a18fc95a
Merged-In: Ia4b33fd4212741152662a2adbb0533bd1b4902ee
(cherry picked from commit 0699cf98042a64e41ee076c464eb115a6579be08)
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 0ab4406..05c9767 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -16,161 +16,161 @@
package com.android.server.connectivity;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.metrics.ConnectStats;
-import android.net.metrics.DnsEvent;
-import android.net.metrics.INetdEventListener;
-import android.net.metrics.IpConnectivityLog;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.system.OsConstants;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-import junit.framework.TestCase;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
+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.fail;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-public class NetdEventListenerServiceTest extends TestCase {
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.OsConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
- // TODO: read from NetdEventListenerService after this constant is read from system property
- static final int BATCH_SIZE = 100;
- static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO;
- // TODO: read from INetdEventListener
- static final int RETURN_CODE = 1;
-
- static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
- static final byte[] RETURN_CODES = new byte[BATCH_SIZE];
- static final int[] LATENCIES = new int[BATCH_SIZE];
- static {
- for (int i = 0; i < BATCH_SIZE; i++) {
- EVENT_TYPES[i] = EVENT_TYPE;
- RETURN_CODES[i] = RETURN_CODE;
- LATENCIES[i] = i;
- }
- }
-
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetdEventListenerServiceTest {
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
NetdEventListenerService mNetdEventListenerService;
+ ConnectivityManager mCm;
- @Mock ConnectivityManager mCm;
- @Mock IpConnectivityLog mLog;
- ArgumentCaptor<NetworkCallback> mCallbackCaptor;
- ArgumentCaptor<DnsEvent> mDnsEvCaptor;
-
+ @Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
- mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
- mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
- mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
-
- verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
+ mCm = mock(ConnectivityManager.class);
+ mNetdEventListenerService = new NetdEventListenerService(mCm);
}
- @SmallTest
- public void testOneDnsBatch() throws Exception {
- log(105, LATENCIES);
- log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
+ @Test
+ public void testDnsLogging() throws Exception {
+ NetworkCapabilities ncWifi = new NetworkCapabilities();
+ NetworkCapabilities ncCell = new NetworkCapabilities();
+ ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- verifyLoggedDnsEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
+ when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
- log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
+ dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
+ dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
+ dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
- mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
- verifyLoggedDnsEvents(
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ String got = flushStatistics();
+ String want = String.join("\n",
+ "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",
+ " transports: 2",
+ " dns_lookup_batch <",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " event_types: 2",
+ " latencies_ms: 3456",
+ " latencies_ms: 267",
+ " latencies_ms: 1230",
+ " latencies_ms: 45",
+ " latencies_ms: 2111",
+ " latencies_ms: 450",
+ " latencies_ms: 638",
+ " latencies_ms: 1300",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 22",
+ " return_codes: 3",
+ " return_codes: 1",
+ " return_codes: 0",
+ " return_codes: 200",
+ " return_codes: 178",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 1",
+ " dns_lookup_batch <",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " event_types: 1",
+ " event_types: 1",
+ " latencies_ms: 56",
+ " latencies_ms: 78",
+ " latencies_ms: 14",
+ " latencies_ms: 56",
+ " latencies_ms: 78",
+ " latencies_ms: 14",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " >",
+ ">",
+ "version: 2\n");
+ assertEquals(want, got);
}
- @SmallTest
- public void testSeveralDmsBatches() throws Exception {
- log(105, LATENCIES);
- log(106, LATENCIES);
- log(105, LATENCIES);
- log(107, LATENCIES);
-
- verifyLoggedDnsEvents(
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
- }
-
- @SmallTest
- public void testDnsBatchAndNetworkLost() throws Exception {
- byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
- byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
- int[] latencies = Arrays.copyOf(LATENCIES, 20);
-
- log(105, LATENCIES);
- log(105, latencies);
- mCallbackCaptor.getValue().onLost(new Network(105));
- log(105, LATENCIES);
-
- verifyLoggedDnsEvents(
- new DnsEvent(105, eventTypes, returnCodes, latencies),
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
- }
-
- @SmallTest
- public void testConcurrentDnsBatchesAndDumps() throws Exception {
- final long stop = System.currentTimeMillis() + 100;
- final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
- new Thread() {
- public void run() {
- while (System.currentTimeMillis() < stop) {
- mNetdEventListenerService.dump(pw);
- }
- }
- }.start();
-
- logDnsAsync(105, LATENCIES);
- logDnsAsync(106, LATENCIES);
- logDnsAsync(107, LATENCIES);
-
- verifyLoggedDnsEvents(500,
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
- }
-
- @SmallTest
- public void testConcurrentDnsBatchesAndNetworkLoss() throws Exception {
- logDnsAsync(105, LATENCIES);
- Thread.sleep(10L);
- // call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
- mCallbackCaptor.getValue().onLost(new Network(105));
-
- // do not verify batch with unpredictable length
- verify(mLog, timeout(500).times(1)).log(any(Parcelable.class));
- }
-
- @SmallTest
+ @Test
public void testConnectLogging() throws Exception {
final int OK = 0;
Thread[] logActions = {
@@ -268,44 +268,18 @@
});
}
- void log(int netId, int[] latencies) {
- try {
- for (int l : latencies) {
- mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
- 0, 0);
+ void dnsEvent(int netId, int type, int result, int latency) throws Exception {
+ mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
+ }
+
+ Thread dumpAction(long durationMs) throws Exception {
+ final long stop = System.currentTimeMillis() + durationMs;
+ final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
+ return new Thread(() -> {
+ while (System.currentTimeMillis() < stop) {
+ mNetdEventListenerService.dump(pw);
}
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- void logDnsAsync(int netId, int[] latencies) {
- new Thread(() -> log(netId, latencies)).start();
- }
-
- void verifyLoggedDnsEvents(DnsEvent... expected) {
- verifyLoggedDnsEvents(0, expected);
- }
-
- void verifyLoggedDnsEvents(int wait, DnsEvent... expectedEvents) {
- verify(mLog, timeout(wait).times(expectedEvents.length)).log(mDnsEvCaptor.capture());
- for (DnsEvent got : mDnsEvCaptor.getAllValues()) {
- OptionalInt index = IntStream.range(0, expectedEvents.length)
- .filter(i -> dnsEventsEqual(expectedEvents[i], got))
- .findFirst();
- // Don't match same expected event more than once.
- index.ifPresent(i -> expectedEvents[i] = null);
- assertTrue(index.isPresent());
- }
- }
-
- /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
- static boolean dnsEventsEqual(DnsEvent expected, DnsEvent got) {
- return (expected == got) || ((expected != null) && (got != null)
- && (expected.netId == got.netId)
- && Arrays.equals(expected.eventTypes, got.eventTypes)
- && Arrays.equals(expected.returnCodes, got.returnCodes)
- && Arrays.equals(expected.latenciesMs, got.latenciesMs));
+ });
}
static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
@@ -318,4 +292,17 @@
fail(e.toString());
}
}
+
+ // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
+ String flushStatistics() throws Exception {
+ IpConnectivityMetrics metricsService =
+ new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
+ metricsService.mNetdListener = mNetdEventListenerService;
+
+ StringWriter buffer = new StringWriter();
+ 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();
+ }
}