Merge changes I1450c126,I8926a922,I54403377,Idf216797 into nyc-mr2-dev
* changes: DO NOT MERGE IP Connectivity metrics: add connect() statistics DO NOT MERGE Fix IP connectivity metrics tests DO NOT MERGE IP connectivity metrics: introduce ConnectStatistics and DNS statistics DO NOT MERGE IP connectivity metrics: add transport field.
This commit is contained in:
@@ -33,6 +33,8 @@ import static com.android.internal.util.Preconditions.checkArgumentPositive;
|
||||
* The available amount of tokens is computed lazily when the bucket state is inspected.
|
||||
* Therefore it is purely synchronous and does not involve any asynchronous activity.
|
||||
* It is not synchronized in any way and not a thread-safe object.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public class TokenBucket {
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 com.android.server.connectivity;
|
||||
|
||||
import android.system.OsConstants;
|
||||
import android.util.IntArray;
|
||||
import android.util.SparseIntArray;
|
||||
import com.android.internal.util.TokenBucket;
|
||||
import com.android.server.connectivity.metrics.IpConnectivityLogClass.ConnectStatistics;
|
||||
import com.android.server.connectivity.metrics.IpConnectivityLogClass.Pair;
|
||||
|
||||
/**
|
||||
* A class that aggregates connect() statistics and helps build
|
||||
* IpConnectivityLogClass.ConnectStatistics instances.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public class ConnectStats {
|
||||
private final static int EALREADY = OsConstants.EALREADY;
|
||||
private final static int EINPROGRESS = OsConstants.EINPROGRESS;
|
||||
|
||||
/** 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();
|
||||
/** TokenBucket for rate limiting latency recording. */
|
||||
private final TokenBucket mLatencyTb;
|
||||
/** Maximum number of latency values recorded. */
|
||||
private final int mMaxLatencyRecords;
|
||||
/** Total count of successful connects. */
|
||||
private int mConnectCount = 0;
|
||||
/** Total count of successful connects with IPv6 socket address. */
|
||||
private int mIpv6ConnectCount = 0;
|
||||
|
||||
public ConnectStats(TokenBucket tb, int maxLatencyRecords) {
|
||||
mLatencyTb = tb;
|
||||
mMaxLatencyRecords = maxLatencyRecords;
|
||||
}
|
||||
|
||||
public ConnectStatistics toProto() {
|
||||
ConnectStatistics stats = new ConnectStatistics();
|
||||
stats.connectCount = mConnectCount;
|
||||
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(ipAddr);
|
||||
countLatency(errno, latencyMs);
|
||||
} else {
|
||||
countError(errno);
|
||||
}
|
||||
}
|
||||
|
||||
private void countConnect(String ipAddr) {
|
||||
mConnectCount++;
|
||||
if (isIPv6(ipAddr)) mIpv6ConnectCount++;
|
||||
}
|
||||
|
||||
private void countLatency(int errno, int ms) {
|
||||
if (isNonBlocking(errno)) {
|
||||
// Ignore connect() on non-blocking sockets
|
||||
return;
|
||||
}
|
||||
if (!mLatencyTb.get()) {
|
||||
// Rate limited
|
||||
return;
|
||||
}
|
||||
if (mLatencies.size() >= mMaxLatencyRecords) {
|
||||
// Hard limit the total number of latency measurements.
|
||||
return;
|
||||
}
|
||||
mLatencies.add(ms);
|
||||
}
|
||||
|
||||
private void countError(int errno) {
|
||||
final int newcount = mErrnos.get(errno, 0) + 1;
|
||||
mErrnos.put(errno, newcount);
|
||||
}
|
||||
|
||||
private static boolean isSuccess(int errno) {
|
||||
return (errno == 0) || isNonBlocking(errno);
|
||||
}
|
||||
|
||||
private static boolean isNonBlocking(int errno) {
|
||||
// On non-blocking TCP sockets, connect() immediately returns EINPROGRESS.
|
||||
// On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY.
|
||||
return (errno == EINPROGRESS) || (errno == EALREADY);
|
||||
}
|
||||
|
||||
private static boolean isIPv6(String ipAddr) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,10 @@ final public class IpConnectivityEventBuilder {
|
||||
private IpConnectivityEventBuilder() {
|
||||
}
|
||||
|
||||
public static byte[] serialize(int dropped, List<ConnectivityMetricsEvent> events)
|
||||
public static byte[] serialize(int dropped, List<IpConnectivityEvent> events)
|
||||
throws IOException {
|
||||
final IpConnectivityLog log = new IpConnectivityLog();
|
||||
log.events = toProto(events);
|
||||
log.events = events.toArray(new IpConnectivityEvent[events.size()]);
|
||||
log.droppedEvents = dropped;
|
||||
if ((log.events.length > 0) || (dropped > 0)) {
|
||||
// Only write version number if log has some information at all.
|
||||
@@ -55,7 +55,7 @@ final public class IpConnectivityEventBuilder {
|
||||
return IpConnectivityLog.toByteArray(log);
|
||||
}
|
||||
|
||||
public static IpConnectivityEvent[] toProto(List<ConnectivityMetricsEvent> eventsIn) {
|
||||
public static List<IpConnectivityEvent> toProto(List<ConnectivityMetricsEvent> eventsIn) {
|
||||
final ArrayList<IpConnectivityEvent> eventsOut = new ArrayList<>(eventsIn.size());
|
||||
for (ConnectivityMetricsEvent in : eventsIn) {
|
||||
final IpConnectivityEvent out = toProto(in);
|
||||
@@ -64,7 +64,7 @@ final public class IpConnectivityEventBuilder {
|
||||
}
|
||||
eventsOut.add(out);
|
||||
}
|
||||
return eventsOut.toArray(new IpConnectivityEvent[eventsOut.size()]);
|
||||
return eventsOut;
|
||||
}
|
||||
|
||||
public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) {
|
||||
|
||||
@@ -36,14 +36,14 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.TokenBucket;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
|
||||
|
||||
/** {@hide} */
|
||||
final public class IpConnectivityMetrics extends SystemService {
|
||||
private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
|
||||
@@ -63,6 +63,8 @@ final public class IpConnectivityMetrics extends SystemService {
|
||||
// Maximum size of the event buffer.
|
||||
private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
|
||||
|
||||
private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000;
|
||||
|
||||
private static final int ERROR_RATE_LIMITED = -1;
|
||||
|
||||
// Lock ensuring that concurrent manipulations of the event buffer are correct.
|
||||
@@ -160,9 +162,15 @@ final public class IpConnectivityMetrics extends SystemService {
|
||||
initBuffer();
|
||||
}
|
||||
|
||||
final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
|
||||
|
||||
if (mNetdListener != null) {
|
||||
mNetdListener.flushStatistics(protoEvents);
|
||||
}
|
||||
|
||||
final byte[] data;
|
||||
try {
|
||||
data = IpConnectivityEventBuilder.serialize(dropped, events);
|
||||
data = IpConnectivityEventBuilder.serialize(dropped, protoEvents);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "could not serialize events", e);
|
||||
return "";
|
||||
|
||||
@@ -19,25 +19,27 @@ package com.android.server.connectivity;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
import android.net.Network;
|
||||
import android.net.INetdEventCallback;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.metrics.DnsEvent;
|
||||
import android.net.metrics.INetdEventListener;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.os.RemoteException;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.util.TokenBucket;
|
||||
import com.android.server.connectivity.metrics.IpConnectivityLogClass.ConnectStatistics;
|
||||
import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of the INetdEventListener interface.
|
||||
*/
|
||||
@@ -52,6 +54,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
// TODO: read this constant from system property
|
||||
private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
|
||||
|
||||
// Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
|
||||
// bursts of 5000 measurements.
|
||||
private static final int CONNECT_LATENCY_BURST_LIMIT = 5000;
|
||||
private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
|
||||
private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
|
||||
|
||||
// Stores the results of a number of consecutive DNS lookups on the same network.
|
||||
// This class is not thread-safe and it is the responsibility of the service to call its methods
|
||||
// on one thread at a time.
|
||||
@@ -121,6 +129,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
}
|
||||
};
|
||||
|
||||
@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")
|
||||
@@ -175,13 +189,28 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
// This method must not block or perform long-running operations.
|
||||
public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
|
||||
int uid) throws RemoteException {
|
||||
maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
|
||||
maybeVerboseLog("onConnectEvent(%d, %d)", netId, latencyMs);
|
||||
|
||||
mConnectStats.addEvent(error, latencyMs, ipAddr);
|
||||
|
||||
if (mNetdEventCallback != null) {
|
||||
mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
|
||||
events.add(flushConnectStats());
|
||||
// TODO: migrate DnsEventBatch to IpConnectivityLogClass.DNSLatencies
|
||||
}
|
||||
|
||||
private IpConnectivityEvent flushConnectStats() {
|
||||
IpConnectivityEvent ev = new IpConnectivityEvent();
|
||||
ev.connectStatistics = mConnectStats.toProto();
|
||||
// TODO: add transport information
|
||||
mConnectStats = makeConnectStats();
|
||||
return ev;
|
||||
}
|
||||
|
||||
public synchronized void dump(PrintWriter writer) {
|
||||
IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
|
||||
pw.println(TAG + ":");
|
||||
@@ -189,9 +218,14 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
for (DnsEventBatch batch : mEventBatches.values()) {
|
||||
pw.println(batch.toString());
|
||||
}
|
||||
// TODO: also dump ConnectStats
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
private ConnectStats makeConnectStats() {
|
||||
return new ConnectStats(mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
|
||||
}
|
||||
|
||||
private static void maybeLog(String s, Object... args) {
|
||||
if (DBG) Log.d(TAG, String.format(s, args));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,22 @@ message NetworkId {
|
||||
optional int32 network_id = 1;
|
||||
};
|
||||
|
||||
// Transport describes a physical technology used by a network. It is a subset
|
||||
// of the TRANSPORT_* constants defined in android.net.NetworkCapabilities.
|
||||
enum Transport {
|
||||
UNKNOWN = 0;
|
||||
BLUETOOTH = 1;
|
||||
CELLULAR = 2;
|
||||
ETHERNET = 3;
|
||||
WIFI = 4;
|
||||
};
|
||||
|
||||
// A pair of (key, value) integers for describing histogram-like statistics.
|
||||
message Pair {
|
||||
optional int32 key = 1;
|
||||
optional int32 value = 2;
|
||||
};
|
||||
|
||||
// Logs changes in the system default network. Changes can be 1) acquiring a
|
||||
// default network with no previous default, 2) a switch of the system default
|
||||
// network to a new default network, 3) a loss of the system default network.
|
||||
@@ -49,7 +65,8 @@ message DefaultNetworkEvent {
|
||||
// This message is associated to android.net.metrics.IpReachabilityEvent.
|
||||
message IpReachabilityEvent {
|
||||
// The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
|
||||
optional string if_name = 1;
|
||||
// Deprecated since version 2, replaced by transport field.
|
||||
optional string if_name = 1 [deprecated = true];
|
||||
|
||||
// The event type code of the probe, represented by constants defined in
|
||||
// android.net.metrics.IpReachabilityEvent.
|
||||
@@ -93,6 +110,7 @@ message ValidationProbeEvent {
|
||||
|
||||
// Logs DNS lookup latencies. Repeated fields must have the same length.
|
||||
// This message is associated to android.net.metrics.DnsEvent.
|
||||
// Deprecated since version 2.
|
||||
message DNSLookupBatch {
|
||||
// The id of the network on which the DNS lookups took place.
|
||||
optional NetworkId network_id = 1;
|
||||
@@ -107,13 +125,62 @@ message DNSLookupBatch {
|
||||
repeated int32 latencies_ms = 4;
|
||||
};
|
||||
|
||||
// Represents a collections of DNS lookup latencies and counters for a
|
||||
// particular combination of DNS query type and return code.
|
||||
// Since version 2.
|
||||
message DNSLatencies {
|
||||
// The type of the DNS lookups, as defined in android.net.metrics.DnsEvent.
|
||||
// Acts as a key for a set of DNS query results.
|
||||
// Possible values are: 0 for getaddrinfo, 1 for gethostbyname.
|
||||
optional int32 type = 1;
|
||||
|
||||
// The return value of the DNS resolver for the DNS lookups.
|
||||
// Acts as a key for a set of DNS query results.
|
||||
// Possible values are: 0 for success, or errno code for failures.
|
||||
optional int32 return_code = 2;
|
||||
|
||||
// The number of query operations recorded.
|
||||
optional int32 query_count = 3;
|
||||
|
||||
// The number of query operations returning A IPv4 records.
|
||||
optional int32 a_count = 4;
|
||||
|
||||
// The number of query operations returning AAAA IPv6 records.
|
||||
optional int32 aaaa_count = 5;
|
||||
|
||||
// The time it took for each DNS lookup to complete. The number of repeated
|
||||
// values can be less than query_count in case of event rate-limiting.
|
||||
repeated int32 latencies_ms = 6;
|
||||
};
|
||||
|
||||
// Represents latency and errno statistics of the connect() system call.
|
||||
// Since version 2.
|
||||
message ConnectStatistics {
|
||||
// The number of connect() operations recorded.
|
||||
optional int32 connect_count = 1;
|
||||
|
||||
// The number of connect() operations with IPv6 socket address.
|
||||
optional int32 ipv6_addr_count = 2;
|
||||
|
||||
// The time it took for each successful connect() operation to complete.
|
||||
// The number of repeated values can be less than connect_count in case of
|
||||
// event rate-limiting.
|
||||
repeated int32 latencies_ms = 3;
|
||||
|
||||
// Counts of all error values returned by failed connect() operations.
|
||||
// The Pair key field is the errno code. The Pair value field is the count
|
||||
// for that errno code.
|
||||
repeated Pair errnos_counters = 4;
|
||||
};
|
||||
|
||||
// Represents a DHCP event on a single interface, which can be a DHCPClient
|
||||
// state transition or a response packet parsing error.
|
||||
// This message is associated to android.net.metrics.DhcpClientEvent and
|
||||
// android.net.metrics.DhcpErrorEvent.
|
||||
message DHCPEvent {
|
||||
// The interface name (wlan, rmnet, lo, ...) on which the event happened.
|
||||
optional string if_name = 1;
|
||||
// Deprecated since version 2, replaced by transport field.
|
||||
optional string if_name = 1 [deprecated = true];
|
||||
|
||||
oneof value {
|
||||
// The name of a state in the DhcpClient state machine, represented by
|
||||
@@ -217,7 +284,8 @@ message RaEvent {
|
||||
// This message is associated to android.net.metrics.IpManagerEvent.
|
||||
message IpProvisioningEvent {
|
||||
// The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
|
||||
optional string if_name = 1;
|
||||
// Deprecated since version 2, replaced by transport field.
|
||||
optional string if_name = 1 [deprecated = true];
|
||||
|
||||
// The code of the IP provisioning event, represented by constants defined in
|
||||
// android.net.metrics.IpManagerEvent.
|
||||
@@ -228,11 +296,15 @@ message IpProvisioningEvent {
|
||||
}
|
||||
|
||||
// Represents one of the IP connectivity event defined in this file.
|
||||
// Next tag: 12
|
||||
// Next tag: 15
|
||||
message IpConnectivityEvent {
|
||||
// Time in ms when the event was recorded.
|
||||
optional int64 time_ms = 1;
|
||||
|
||||
// Physical transport of the network on which the event happened.
|
||||
// Since version 2.
|
||||
optional Transport transport = 12;
|
||||
|
||||
// Event type.
|
||||
oneof event {
|
||||
|
||||
@@ -246,7 +318,14 @@ message IpConnectivityEvent {
|
||||
NetworkEvent network_event = 4;
|
||||
|
||||
// A batch of DNS lookups.
|
||||
DNSLookupBatch dns_lookup_batch = 5;
|
||||
// Deprecated in the nyc-mr2 release since version 2, and replaced by dns_latencies.
|
||||
DNSLookupBatch dns_lookup_batch = 5 [deprecated = true];
|
||||
|
||||
// DNS lookup latency statistics.
|
||||
DNSLatencies dns_latencies = 13;
|
||||
|
||||
// Connect latency and errno statistics.
|
||||
ConnectStatistics connect_statistics = 14;
|
||||
|
||||
// A DHCP client event or DHCP receive error.
|
||||
DHCPEvent dhcp_event = 6;
|
||||
@@ -277,9 +356,9 @@ message IpConnectivityLog {
|
||||
optional int32 dropped_events = 2;
|
||||
|
||||
// The version number of the metrics events being collected.
|
||||
// nyc-dev: not populated, implicitly 0
|
||||
// nyc-dr1: not populated, implicitly 1 (sailfish and marlin only)
|
||||
// nyc-mr1: not populated, implicitly 1
|
||||
// nyc-mr2: 2
|
||||
// nyc-dev: not populated, implicitly 0.
|
||||
// nyc-dr1: not populated, implicitly 1 (sailfish and marlin only).
|
||||
// nyc-mr1: not populated, implicitly 1.
|
||||
// nyc-mr2: 2.
|
||||
optional int32 version = 3;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,17 @@
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aBool;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aLong;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aString;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aType;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.anInt;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.b;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
|
||||
import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
|
||||
|
||||
import android.net.ConnectivityMetricsEvent;
|
||||
import android.net.metrics.ApfProgramEvent;
|
||||
import android.net.metrics.ApfStats;
|
||||
@@ -28,24 +39,13 @@ import android.net.metrics.IpReachabilityEvent;
|
||||
import android.net.metrics.NetworkEvent;
|
||||
import android.net.metrics.RaEvent;
|
||||
import android.net.metrics.ValidationProbeEvent;
|
||||
import com.google.protobuf.nano.MessageNano;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import java.util.Arrays;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aBool;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aLong;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aString;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.aType;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.anInt;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.b;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
|
||||
import static com.android.server.connectivity.MetricsTestUtil.ipEv;
|
||||
|
||||
public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
|
||||
@SmallTest
|
||||
public void testDefaultNetworkEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(DefaultNetworkEvent.class),
|
||||
@@ -58,6 +58,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" default_network_event <",
|
||||
" network_id <",
|
||||
" network_id: 102",
|
||||
@@ -70,13 +72,13 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
" transport_types: 2",
|
||||
" transport_types: 3",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDhcpClientEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(DhcpClientEvent.class),
|
||||
@@ -87,19 +89,20 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" dhcp_event <",
|
||||
" duration_ms: 192",
|
||||
" error_code: 0",
|
||||
" if_name: \"wlan0\"",
|
||||
" state_transition: \"SomeState\"",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDhcpErrorEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(DhcpErrorEvent.class),
|
||||
@@ -109,19 +112,20 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" dhcp_event <",
|
||||
" duration_ms: 0",
|
||||
" error_code: 50397184",
|
||||
" if_name: \"wlan0\"",
|
||||
" state_transition: \"\"",
|
||||
" error_code: 50397184",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDnsEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(DnsEvent.class),
|
||||
@@ -133,6 +137,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" dns_lookup_batch <",
|
||||
" event_types: 1",
|
||||
" event_types: 1",
|
||||
@@ -162,13 +168,13 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
" return_codes: 200",
|
||||
" return_codes: 178",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testIpManagerEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(IpManagerEvent.class),
|
||||
@@ -179,18 +185,20 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" ip_provisioning_event <",
|
||||
" event_type: 1",
|
||||
" if_name: \"wlan0\"",
|
||||
" latency_ms: 5678",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testIpReachabilityEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(IpReachabilityEvent.class),
|
||||
@@ -200,17 +208,19 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" ip_reachability_event <",
|
||||
" event_type: 512",
|
||||
" if_name: \"wlan0\"",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testNetworkEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(NetworkEvent.class),
|
||||
@@ -221,6 +231,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" network_event <",
|
||||
" event_type: 5",
|
||||
" latency_ms: 20410",
|
||||
@@ -228,13 +240,13 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
" network_id: 100",
|
||||
" >",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testValidationProbeEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(ValidationProbeEvent.class),
|
||||
@@ -247,6 +259,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" validation_probe_event <",
|
||||
" latency_ms: 40730",
|
||||
" network_id <",
|
||||
@@ -261,6 +274,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testApfProgramEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(ApfProgramEvent.class),
|
||||
@@ -273,6 +287,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" apf_program_event <",
|
||||
" current_ras: 9",
|
||||
" drop_multicast: true",
|
||||
@@ -281,13 +297,13 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
" lifetime: 200",
|
||||
" program_length: 2048",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testApfStatsSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(ApfStats.class),
|
||||
@@ -303,6 +319,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" apf_statistics <",
|
||||
" dropped_ras: 2",
|
||||
" duration_ms: 45000",
|
||||
@@ -313,13 +331,13 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
" received_ras: 10",
|
||||
" zero_lifetime_ras: 1",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
verifySerialization(want, ev);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testRaEventSerialization() {
|
||||
ConnectivityMetricsEvent ev = describeIpEvent(
|
||||
aType(RaEvent.class),
|
||||
@@ -333,6 +351,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 1",
|
||||
" transport: 0",
|
||||
" ra_event <",
|
||||
" dnssl_lifetime: -1",
|
||||
" prefix_preferred_lifetime: 300",
|
||||
@@ -341,7 +361,6 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
" route_info_lifetime: -1",
|
||||
" router_lifetime: 2000",
|
||||
" >",
|
||||
" time_ms: 1",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
@@ -350,9 +369,9 @@ public class IpConnectivityEventBuilderTest extends TestCase {
|
||||
|
||||
static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
|
||||
try {
|
||||
byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
|
||||
IpConnectivityLog log = new IpConnectivityLog();
|
||||
MessageNano.mergeFrom(log, got);
|
||||
byte[] got = IpConnectivityEventBuilder.serialize(0,
|
||||
IpConnectivityEventBuilder.toProto(Arrays.asList(input)));
|
||||
IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
|
||||
assertEquals(want, log.toString());
|
||||
} catch (Exception e) {
|
||||
fail(e.toString());
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityMetricsEvent;
|
||||
import android.net.IIpConnectivityMetrics;
|
||||
@@ -29,9 +32,9 @@ import android.net.metrics.IpReachabilityEvent;
|
||||
import android.net.metrics.RaEvent;
|
||||
import android.net.metrics.ValidationProbeEvent;
|
||||
import android.os.Parcelable;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Base64;
|
||||
import com.android.server.connectivity.metrics.IpConnectivityLogClass;
|
||||
import com.google.protobuf.nano.MessageNano;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
@@ -43,10 +46,6 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class IpConnectivityMetricsTest extends TestCase {
|
||||
static final IpReachabilityEvent FAKE_EV =
|
||||
new IpReachabilityEvent("wlan0", IpReachabilityEvent.NUD_FAILED);
|
||||
@@ -61,6 +60,7 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLoggingEvents() throws Exception {
|
||||
IpConnectivityLog logger = new IpConnectivityLog(mMockService);
|
||||
|
||||
@@ -74,6 +74,7 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
assertEventsEqual(expectedEvent(3), got.get(2));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLoggingEventsWithMultipleCallers() throws Exception {
|
||||
IpConnectivityLog logger = new IpConnectivityLog(mMockService);
|
||||
|
||||
@@ -101,6 +102,7 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testBufferFlushing() {
|
||||
String output1 = getdump("flush");
|
||||
assertEquals("", output1);
|
||||
@@ -113,6 +115,7 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
assertEquals("", output3);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testRateLimiting() {
|
||||
final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
|
||||
final ApfProgramEvent ev = new ApfProgramEvent(0, 0, 0, 0, 0);
|
||||
@@ -134,6 +137,7 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
assertEquals("", output2);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testEndToEndLogging() {
|
||||
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
|
||||
|
||||
@@ -154,22 +158,25 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
String want = joinLines(
|
||||
"dropped_events: 0",
|
||||
"events <",
|
||||
" time_ms: 100",
|
||||
" transport: 0",
|
||||
" ip_reachability_event <",
|
||||
" event_type: 512",
|
||||
" if_name: \"wlan0\"",
|
||||
" >",
|
||||
" time_ms: 100",
|
||||
">",
|
||||
"events <",
|
||||
" time_ms: 200",
|
||||
" transport: 0",
|
||||
" dhcp_event <",
|
||||
" duration_ms: 192",
|
||||
" error_code: 0",
|
||||
" if_name: \"wlan0\"",
|
||||
" state_transition: \"SomeState\"",
|
||||
" >",
|
||||
" time_ms: 200",
|
||||
">",
|
||||
"events <",
|
||||
" time_ms: 300",
|
||||
" transport: 0",
|
||||
" default_network_event <",
|
||||
" network_id <",
|
||||
" network_id: 102",
|
||||
@@ -182,18 +189,19 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
" transport_types: 2",
|
||||
" transport_types: 3",
|
||||
" >",
|
||||
" time_ms: 300",
|
||||
">",
|
||||
"events <",
|
||||
" time_ms: 400",
|
||||
" transport: 0",
|
||||
" ip_provisioning_event <",
|
||||
" event_type: 1",
|
||||
" if_name: \"wlan0\"",
|
||||
" latency_ms: 5678",
|
||||
" >",
|
||||
" time_ms: 400",
|
||||
">",
|
||||
"events <",
|
||||
" time_ms: 500",
|
||||
" transport: 0",
|
||||
" validation_probe_event <",
|
||||
" latency_ms: 40730",
|
||||
" network_id <",
|
||||
@@ -204,6 +212,8 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
" >",
|
||||
">",
|
||||
"events <",
|
||||
" time_ms: 600",
|
||||
" transport: 0",
|
||||
" apf_statistics <",
|
||||
" dropped_ras: 2",
|
||||
" duration_ms: 45000",
|
||||
@@ -214,9 +224,10 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
" received_ras: 10",
|
||||
" zero_lifetime_ras: 1",
|
||||
" >",
|
||||
" time_ms: 600",
|
||||
">",
|
||||
"events <",
|
||||
" time_ms: 700",
|
||||
" transport: 0",
|
||||
" ra_event <",
|
||||
" dnssl_lifetime: -1",
|
||||
" prefix_preferred_lifetime: 300",
|
||||
@@ -225,7 +236,6 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
" route_info_lifetime: -1",
|
||||
" router_lifetime: 2000",
|
||||
" >",
|
||||
" time_ms: 700",
|
||||
">",
|
||||
"version: 2");
|
||||
|
||||
@@ -254,8 +264,7 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
try {
|
||||
byte[] got = Base64.decode(output, Base64.DEFAULT);
|
||||
IpConnectivityLogClass.IpConnectivityLog log =
|
||||
new IpConnectivityLogClass.IpConnectivityLog();
|
||||
MessageNano.mergeFrom(log, got);
|
||||
IpConnectivityLogClass.IpConnectivityLog.parseFrom(got);
|
||||
assertEquals(want, log.toString());
|
||||
} catch (Exception e) {
|
||||
fail(e.toString());
|
||||
@@ -283,10 +292,5 @@ public class IpConnectivityMetricsTest extends TestCase {
|
||||
}
|
||||
|
||||
static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
|
||||
new Comparator<ConnectivityMetricsEvent>() {
|
||||
@Override
|
||||
public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) {
|
||||
return (int) (ev1.timestamp - ev2.timestamp);
|
||||
}
|
||||
};
|
||||
Comparator.comparingLong((ev) -> ev.timestamp);
|
||||
}
|
||||
|
||||
@@ -16,24 +16,34 @@
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
import android.net.Network;
|
||||
import android.net.metrics.DnsEvent;
|
||||
import android.net.metrics.INetdEventListener;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import android.system.OsConstants;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import com.android.server.connectivity.metrics.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 static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
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 org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.eq;
|
||||
@@ -41,13 +51,6 @@ import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class NetdEventListenerServiceTest extends TestCase {
|
||||
|
||||
// TODO: read from NetdEventListenerService after this constant is read from system property
|
||||
@@ -67,50 +70,56 @@ public class NetdEventListenerServiceTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String EXAMPLE_IPV4 = "192.0.2.1";
|
||||
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
|
||||
|
||||
NetdEventListenerService mNetdEventListenerService;
|
||||
|
||||
@Mock ConnectivityManager mCm;
|
||||
@Mock IpConnectivityLog mLog;
|
||||
ArgumentCaptor<NetworkCallback> mCallbackCaptor;
|
||||
ArgumentCaptor<DnsEvent> mEvCaptor;
|
||||
ArgumentCaptor<DnsEvent> mDnsEvCaptor;
|
||||
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
|
||||
mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
|
||||
mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
|
||||
mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
|
||||
|
||||
verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
|
||||
}
|
||||
|
||||
public void testOneBatch() throws Exception {
|
||||
@SmallTest
|
||||
public void testOneDnsBatch() throws Exception {
|
||||
log(105, LATENCIES);
|
||||
log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
|
||||
|
||||
verifyLoggedEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
|
||||
verifyLoggedDnsEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
|
||||
|
||||
log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
|
||||
|
||||
mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
|
||||
verifyLoggedEvents(
|
||||
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));
|
||||
}
|
||||
|
||||
public void testSeveralBatches() throws Exception {
|
||||
@SmallTest
|
||||
public void testSeveralDmsBatches() throws Exception {
|
||||
log(105, LATENCIES);
|
||||
log(106, LATENCIES);
|
||||
log(105, LATENCIES);
|
||||
log(107, LATENCIES);
|
||||
|
||||
verifyLoggedEvents(
|
||||
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));
|
||||
}
|
||||
|
||||
public void testBatchAndNetworkLost() throws Exception {
|
||||
@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);
|
||||
@@ -120,13 +129,14 @@ public class NetdEventListenerServiceTest extends TestCase {
|
||||
mCallbackCaptor.getValue().onLost(new Network(105));
|
||||
log(105, LATENCIES);
|
||||
|
||||
verifyLoggedEvents(
|
||||
verifyLoggedDnsEvents(
|
||||
new DnsEvent(105, eventTypes, returnCodes, latencies),
|
||||
new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
|
||||
new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
|
||||
}
|
||||
|
||||
public void testConcurrentBatchesAndDumps() throws Exception {
|
||||
@SmallTest
|
||||
public void testConcurrentDnsBatchesAndDumps() throws Exception {
|
||||
final long stop = System.currentTimeMillis() + 100;
|
||||
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
|
||||
new Thread() {
|
||||
@@ -137,26 +147,120 @@ public class NetdEventListenerServiceTest extends TestCase {
|
||||
}
|
||||
}.start();
|
||||
|
||||
logAsync(105, LATENCIES);
|
||||
logAsync(106, LATENCIES);
|
||||
logAsync(107, LATENCIES);
|
||||
logDnsAsync(105, LATENCIES);
|
||||
logDnsAsync(106, LATENCIES);
|
||||
logDnsAsync(107, LATENCIES);
|
||||
|
||||
verifyLoggedEvents(500,
|
||||
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));
|
||||
}
|
||||
|
||||
public void testConcurrentBatchesAndNetworkLoss() throws Exception {
|
||||
logAsync(105, LATENCIES);
|
||||
@SmallTest
|
||||
public void testConcurrentDnsBatchesAndNetworkLoss() throws Exception {
|
||||
logDnsAsync(105, LATENCIES);
|
||||
Thread.sleep(10L);
|
||||
// call onLost() asynchronously to logAsync's onDnsEvent() calls.
|
||||
// call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
|
||||
mCallbackCaptor.getValue().onLost(new Network(105));
|
||||
|
||||
// do not verify unpredictable batch
|
||||
verify(mLog, timeout(500).times(1)).log(any());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testConnectLogging() throws Exception {
|
||||
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),
|
||||
// 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),
|
||||
// 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),
|
||||
};
|
||||
|
||||
for (Thread t : logActions) {
|
||||
t.start();
|
||||
}
|
||||
for (Thread t : logActions) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
List<IpConnectivityEvent> events = new ArrayList<>();
|
||||
mNetdEventListenerService.flushStatistics(events);
|
||||
|
||||
IpConnectivityEvent got = events.get(0);
|
||||
String want = joinLines(
|
||||
"time_ms: 0",
|
||||
"transport: 0",
|
||||
"connect_statistics <",
|
||||
" 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",
|
||||
" >",
|
||||
" 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");
|
||||
verifyConnectEvent(want, got);
|
||||
}
|
||||
|
||||
Thread connectEventAction(int error, int latencyMs, String ipAddr) {
|
||||
return new Thread(() -> {
|
||||
try {
|
||||
mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1);
|
||||
} catch (Exception e) {
|
||||
fail(e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void log(int netId, int[] latencies) {
|
||||
try {
|
||||
for (int l : latencies) {
|
||||
@@ -168,7 +272,7 @@ public class NetdEventListenerServiceTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
void logAsync(int netId, int[] latencies) {
|
||||
void logDnsAsync(int netId, int[] latencies) {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
log(netId, latencies);
|
||||
@@ -176,15 +280,15 @@ public class NetdEventListenerServiceTest extends TestCase {
|
||||
}.start();
|
||||
}
|
||||
|
||||
void verifyLoggedEvents(DnsEvent... expected) {
|
||||
verifyLoggedEvents(0, expected);
|
||||
void verifyLoggedDnsEvents(DnsEvent... expected) {
|
||||
verifyLoggedDnsEvents(0, expected);
|
||||
}
|
||||
|
||||
void verifyLoggedEvents(int wait, DnsEvent... expectedEvents) {
|
||||
verify(mLog, timeout(wait).times(expectedEvents.length)).log(mEvCaptor.capture());
|
||||
for (DnsEvent got : mEvCaptor.getAllValues()) {
|
||||
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 -> eventsEqual(expectedEvents[i], got))
|
||||
.filter(i -> dnsEventsEqual(expectedEvents[i], got))
|
||||
.findFirst();
|
||||
// Don't match same expected event more than once.
|
||||
index.ifPresent(i -> expectedEvents[i] = null);
|
||||
@@ -193,11 +297,30 @@ public class NetdEventListenerServiceTest extends TestCase {
|
||||
}
|
||||
|
||||
/** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
|
||||
static boolean eventsEqual(DnsEvent expected, DnsEvent got) {
|
||||
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 String joinLines(String ... elems) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (String s : elems) {
|
||||
b.append(s).append("\n");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
|
||||
try {
|
||||
Arrays.sort(got.connectStatistics.latenciesMs);
|
||||
Arrays.sort(got.connectStatistics.errnosCounters,
|
||||
Comparator.comparingInt((p) -> p.key));
|
||||
assertEquals(expected, got.toString());
|
||||
} catch (Exception e) {
|
||||
fail(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user