From 50a84c6210f4f165b489dd1bf1c535d86dda2fba Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Fri, 2 Sep 2016 09:00:59 +0900 Subject: [PATCH] Add ipconnectivity.proto to services jar This patch moves ipconnectivity.proto and companion serialization classes from /vendor/google/app/ConnectivityMetrics into the framework services jar. This will allow to implement a dumpsys interface for IpConnectivity metrics events defined in android.net.metrics that output metrics as a serialized proto. Bug: 31254800 Change-Id: Ia4449103fd7222c9d899352fea2dce47a790a49c --- services/core/Android.mk | 2 + .../IpConnectivityEventBuilder.java | 257 +++++++++++++ services/core/proto/ipconnectivity.proto | 274 +++++++++++++ .../IpConnectivityEventBuilderTest.java | 359 ++++++++++++++++++ .../server/connectivity/MetricsTestUtil.java | 120 ++++++ 5 files changed, 1012 insertions(+) create mode 100644 services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java create mode 100644 services/core/proto/ipconnectivity.proto create mode 100644 services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java create mode 100644 services/tests/servicestests/src/com/android/server/connectivity/MetricsTestUtil.java diff --git a/services/core/Android.mk b/services/core/Android.mk index b965ce39e7d59..32105429575da 100644 --- a/services/core/Android.mk +++ b/services/core/Android.mk @@ -8,6 +8,7 @@ LOCAL_AIDL_INCLUDES := system/netd/server/binder LOCAL_SRC_FILES += \ $(call all-java-files-under,java) \ + $(call all-proto-files-under, proto) \ java/com/android/server/EventLogTags.logtags \ java/com/android/server/am/EventLogTags.logtags \ ../../../../system/netd/server/binder/android/net/INetd.aidl \ @@ -18,6 +19,7 @@ LOCAL_AIDL_INCLUDES += \ LOCAL_JAVA_LIBRARIES := services.net telephony-common LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update +LOCAL_PROTOC_OPTIMIZE_TYPE := nano ifneq ($(INCREMENTAL_BUILDS),) LOCAL_PROGUARD_ENABLED := disabled diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java new file mode 100644 index 0000000000000..f1ef947f34726 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java @@ -0,0 +1,257 @@ +/* + * 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.net.ConnectivityMetricsEvent; +import android.net.metrics.ApfProgramEvent; +import android.net.metrics.ApfStats; +import android.net.metrics.DefaultNetworkEvent; +import android.net.metrics.DhcpClientEvent; +import android.net.metrics.DhcpErrorEvent; +import android.net.metrics.DnsEvent; +import android.net.metrics.IpManagerEvent; +import android.net.metrics.IpReachabilityEvent; +import android.net.metrics.NetworkEvent; +import android.net.metrics.RaEvent; +import android.net.metrics.ValidationProbeEvent; +import android.os.Parcelable; +import com.android.server.connectivity.metrics.IpConnectivityLogClass; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; +import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog; +import static com.android.server.connectivity.metrics.IpConnectivityLogClass.NetworkId; + +/** {@hide} */ +final public class IpConnectivityEventBuilder { + private IpConnectivityEventBuilder() { + } + + public static byte[] serialize(int dropped, List events) + throws IOException { + final IpConnectivityLog log = new IpConnectivityLog(); + log.events = toProto(events); + log.droppedEvents = dropped; + return IpConnectivityLog.toByteArray(log); + } + + public static IpConnectivityEvent[] toProto(List eventsIn) { + final ArrayList eventsOut = new ArrayList<>(eventsIn.size()); + for (ConnectivityMetricsEvent in : eventsIn) { + final IpConnectivityEvent out = toProto(in); + if (out == null) { + continue; + } + eventsOut.add(out); + } + return eventsOut.toArray(new IpConnectivityEvent[eventsOut.size()]); + } + + public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) { + final IpConnectivityEvent out = new IpConnectivityEvent(); + if (!setEvent(out, ev.data)) { + return null; + } + out.timeMs = ev.timestamp; + return out; + } + + private static boolean setEvent(IpConnectivityEvent out, Parcelable in) { + if (in instanceof DhcpErrorEvent) { + setDhcpErrorEvent(out, (DhcpErrorEvent) in); + return true; + } + + if (in instanceof DhcpClientEvent) { + setDhcpClientEvent(out, (DhcpClientEvent) in); + return true; + } + + if (in instanceof DnsEvent) { + setDnsEvent(out, (DnsEvent) in); + return true; + } + + if (in instanceof IpManagerEvent) { + setIpManagerEvent(out, (IpManagerEvent) in); + return true; + } + + if (in instanceof IpReachabilityEvent) { + setIpReachabilityEvent(out, (IpReachabilityEvent) in); + return true; + } + + if (in instanceof DefaultNetworkEvent) { + setDefaultNetworkEvent(out, (DefaultNetworkEvent) in); + return true; + } + + if (in instanceof NetworkEvent) { + setNetworkEvent(out, (NetworkEvent) in); + return true; + } + + if (in instanceof ValidationProbeEvent) { + setValidationProbeEvent(out, (ValidationProbeEvent) in); + return true; + } + + if (in instanceof ApfProgramEvent) { + setApfProgramEvent(out, (ApfProgramEvent) in); + return true; + } + + if (in instanceof ApfStats) { + setApfStats(out, (ApfStats) in); + return true; + } + + if (in instanceof RaEvent) { + setRaEvent(out, (RaEvent) in); + return true; + } + + return false; + } + + private static void setDhcpErrorEvent(IpConnectivityEvent out, DhcpErrorEvent in) { + out.dhcpEvent = new IpConnectivityLogClass.DHCPEvent(); + out.dhcpEvent.ifName = in.ifName; + out.dhcpEvent.errorCode = in.errorCode; + } + + private static void setDhcpClientEvent(IpConnectivityEvent out, DhcpClientEvent in) { + out.dhcpEvent = new IpConnectivityLogClass.DHCPEvent(); + out.dhcpEvent.ifName = in.ifName; + out.dhcpEvent.stateTransition = in.msg; + out.dhcpEvent.durationMs = in.durationMs; + } + + private static void setDnsEvent(IpConnectivityEvent out, DnsEvent in) { + out.dnsLookupBatch = new IpConnectivityLogClass.DNSLookupBatch(); + out.dnsLookupBatch.networkId = netIdOf(in.netId); + out.dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes); + out.dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes); + out.dnsLookupBatch.latenciesMs = in.latenciesMs; + } + + private static void setIpManagerEvent(IpConnectivityEvent out, IpManagerEvent in) { + out.ipProvisioningEvent = new IpConnectivityLogClass.IpProvisioningEvent(); + out.ipProvisioningEvent.ifName = in.ifName; + out.ipProvisioningEvent.eventType = in.eventType; + out.ipProvisioningEvent.latencyMs = (int) in.durationMs; + } + + private static void setIpReachabilityEvent(IpConnectivityEvent out, IpReachabilityEvent in) { + out.ipReachabilityEvent = new IpConnectivityLogClass.IpReachabilityEvent(); + out.ipReachabilityEvent.ifName = in.ifName; + out.ipReachabilityEvent.eventType = in.eventType; + } + + private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) { + out.defaultNetworkEvent = new IpConnectivityLogClass.DefaultNetworkEvent(); + out.defaultNetworkEvent.networkId = netIdOf(in.netId); + out.defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId); + out.defaultNetworkEvent.transportTypes = in.transportTypes; + out.defaultNetworkEvent.previousNetworkIpSupport = ipSupportOf(in); + } + + private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) { + out.networkEvent = new IpConnectivityLogClass.NetworkEvent(); + out.networkEvent.networkId = netIdOf(in.netId); + out.networkEvent.eventType = in.eventType; + out.networkEvent.latencyMs = (int) in.durationMs; + } + + private static void setValidationProbeEvent(IpConnectivityEvent out, ValidationProbeEvent in) { + out.validationProbeEvent = new IpConnectivityLogClass.ValidationProbeEvent(); + out.validationProbeEvent.networkId = netIdOf(in.netId); + out.validationProbeEvent.latencyMs = (int) in.durationMs; + out.validationProbeEvent.probeType = in.probeType; + out.validationProbeEvent.probeResult = in.returnCode; + } + + private static void setApfProgramEvent(IpConnectivityEvent out, ApfProgramEvent in) { + out.apfProgramEvent = new IpConnectivityLogClass.ApfProgramEvent(); + out.apfProgramEvent.lifetime = in.lifetime; + out.apfProgramEvent.filteredRas = in.filteredRas; + out.apfProgramEvent.currentRas = in.currentRas; + out.apfProgramEvent.programLength = in.programLength; + if (isBitSet(in.flags, ApfProgramEvent.FLAG_MULTICAST_FILTER_ON)) { + out.apfProgramEvent.dropMulticast = true; + } + if (isBitSet(in.flags, ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS)) { + out.apfProgramEvent.hasIpv4Addr = true; + } + } + + private static void setApfStats(IpConnectivityEvent out, ApfStats in) { + out.apfStatistics = new IpConnectivityLogClass.ApfStatistics(); + out.apfStatistics.durationMs = in.durationMs; + out.apfStatistics.receivedRas = in.receivedRas; + out.apfStatistics.matchingRas = in.matchingRas; + out.apfStatistics.droppedRas = in.droppedRas; + out.apfStatistics.zeroLifetimeRas = in.zeroLifetimeRas; + out.apfStatistics.parseErrors = in.parseErrors; + out.apfStatistics.programUpdates = in.programUpdates; + out.apfStatistics.maxProgramSize = in.maxProgramSize; + } + + private static void setRaEvent(IpConnectivityEvent out, RaEvent in) { + out.raEvent = new IpConnectivityLogClass.RaEvent(); + out.raEvent.routerLifetime = in.routerLifetime; + out.raEvent.prefixValidLifetime = in.prefixValidLifetime; + out.raEvent.prefixPreferredLifetime = in.prefixPreferredLifetime; + out.raEvent.routeInfoLifetime = in.routeInfoLifetime; + out.raEvent.rdnssLifetime = in.rdnssLifetime; + out.raEvent.dnsslLifetime = in.dnsslLifetime; + } + + private static int[] bytesToInts(byte[] in) { + final int[] out = new int[in.length]; + for (int i = 0; i < in.length; i++) { + out[i] = in[i] & 0xFF; + } + return out; + } + + private static NetworkId netIdOf(int netid) { + final NetworkId ni = new NetworkId(); + ni.networkId = netid; + return ni; + } + + private static int ipSupportOf(DefaultNetworkEvent in) { + if (in.prevIPv4 && in.prevIPv6) { + return IpConnectivityLogClass.DefaultNetworkEvent.DUAL; + } + if (in.prevIPv6) { + return IpConnectivityLogClass.DefaultNetworkEvent.IPV6; + } + if (in.prevIPv4) { + return IpConnectivityLogClass.DefaultNetworkEvent.IPV4; + } + return IpConnectivityLogClass.DefaultNetworkEvent.NONE; + } + + private static boolean isBitSet(int flags, int bit) { + return (flags & (1 << bit)) != 0; + } +} diff --git a/services/core/proto/ipconnectivity.proto b/services/core/proto/ipconnectivity.proto new file mode 100644 index 0000000000000..e0d7f0984eb0d --- /dev/null +++ b/services/core/proto/ipconnectivity.proto @@ -0,0 +1,274 @@ +// LINT: LEGACY_NAMES +syntax = "proto2"; + +package clearcut.connectivity; + +option java_package = "com.android.server.connectivity.metrics"; +option java_outer_classname = "IpConnectivityLogClass"; + +// NetworkId represents the id given by the system to a physical network on the +// Android device. It is used to relates events to each other for devices with +// multiple networks (WiFi, 4G, ...). +message NetworkId { + // Every network gets assigned a network_id on creation based on order of + // creation. Thus network_id N is assigned to the network created directly + // after network N-1. Thus there is no PII involved here. Zero means no + // network. The value 0 is never assigned to a network. + optional int32 network_id = 1; +}; + +// 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. +// This message is associated to android.net.metrics.DefaultNetworkEvent. +message DefaultNetworkEvent { + // A value of 0 means this is a loss of the system default network. + optional NetworkId network_id = 1; + + // A value of 0 means there was no previous default network. + optional NetworkId previous_network_id = 2; + + // Whether the network supports IPv4, IPv6, or both. + enum IPSupport { + NONE = 0; + IPV4 = 1; + IPV6 = 2; + DUAL = 3; + }; + + // Best available information about IP support of the previous network when + // disconnecting or switching to a new default network. + optional IPSupport previous_network_ip_support = 3; + + // The transport types of the new default network, represented by + // TRANSPORT_* constants as defined in NetworkCapabilities. + repeated int32 transport_types = 4; +}; + +// Logs IpReachabilityMonitor probe events and NUD_FAILED events. +// 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; + + // The event type code of the probe, represented by constants defined in + // android.net.metrics.IpReachabilityEvent. + optional int32 event_type = 2; +}; + +// Logs NetworkMonitor and ConnectivityService events related to the state of +// a network: connection, evaluation, validation, lingering, and disconnection. +// This message is associated to android.net.metrics.NetworkEvent. +message NetworkEvent { + // The id of the network on which this event happened. + optional NetworkId network_id = 1; + + // The type of network event, represented by NETWORK_* constants defined in + // android.net.metrics.NetworkEvent. + optional int32 event_type = 2; + + // Only valid after finishing evaluating a network for Internet connectivity. + // The time it took for this evaluation to complete. + optional int32 latency_ms = 3; +} + +// Logs individual captive portal probing events that are performed when +// evaluating or reevaluating networks for Internet connectivity. +// This message is associated to android.net.metrics.ValidationProbeEvent. +message ValidationProbeEvent { + // The id of the network for which the probe was sent. + optional NetworkId network_id = 1; + + // The time it took for that probe to complete or time out. + optional int32 latency_ms = 2; + + // The type of portal probe, represented by PROBE_* constants defined in + // android.net.metrics.ValidationProbeEvent. + optional int32 probe_type = 3; + + // The http code result of the probe test. + optional int32 probe_result = 4; +} + +// Logs DNS lookup latencies. Repeated fields must have the same length. +// This message is associated to android.net.metrics.DnsEvent. +message DNSLookupBatch { + // The id of the network on which the DNS lookups took place. + optional NetworkId network_id = 1; + + // The types of the DNS lookups, as defined in android.net.metrics.DnsEvent. + repeated int32 event_types = 2; + + // The return values of the DNS resolver for each DNS lookups. + repeated int32 return_codes = 3; + + // The time it took for each DNS lookups to complete. + repeated int32 latencies_ms = 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; + + oneof value { + // The name of a state in the DhcpClient state machine, represented by + // the inner classes of android.net.dhcp.DhcpClient. + string state_transition = 2; + + // The error code of a DHCP error, represented by constants defined in + // android.net.metrics.DhcpErrorEvent. + int32 error_code = 3; + } + + // Lifetime duration in milliseconds of a DhcpClient state, or transition + // time in milliseconds between specific pairs of DhcpClient's states. + // Only populated when state_transition is populated. + optional int32 duration_ms = 4; +} + +// Represents the generation of an Android Packet Filter program. +message ApfProgramEvent { + // Lifetime of the program in seconds. + optional int64 lifetime = 1; + + // Number of RAs filtered by the APF program. + optional int32 filtered_ras = 2; + + // Total number of RAs to filter currently tracked by ApfFilter. Can be more + // than filtered_ras if all available program size was exhausted. + optional int32 current_ras = 3; + + // Length of the APF program in bytes. + optional int32 program_length = 4; + + // True if the APF program is dropping multicast and broadcast traffic. + optional bool drop_multicast = 5; + + // True if the interface on which APF runs has an IPv4 address. + optional bool has_ipv4_addr = 6; +} + +// Represents Router Advertisement listening statistics for an interface with +// Android Packet Filter enabled. +message ApfStatistics { + // The time interval in milliseconds these stastistics cover. + optional int64 duration_ms = 1; + + // The total number of received RAs. + optional int32 received_ras = 2; + + // The total number of received RAs that matched a known RA. + optional int32 matching_ras = 3; + + // The total number of received RAs ignored due to the MAX_RAS limit. + optional int32 dropped_ras = 5; + + // The total number of received RAs with an effective lifetime of 0 seconds. + // Effective lifetime for APF is the minimum of all lifetimes in a RA. + optional int32 zero_lifetime_ras = 6; + + // The total number of received RAs that could not be parsed. + optional int32 parse_errors = 7; + + // The total number of APF program updates triggered by an RA reception. + optional int32 program_updates = 8; + + // The maximum APF program size in byte advertised by hardware. + optional int32 max_program_size = 9; +} + +// Represents the reception of a Router Advertisement packet for an interface +// with Android Packet Filter enabled. +message RaEvent { + // All lifetime values are expressed in seconds. The default value for an + // option lifetime that was not present in the RA option list is -1. + // The lifetime of an option (e.g., the Prefix Information Option) is the + // minimum lifetime of all such options in the packet. + + // The value of the router lifetime in the RA packet. + optional int64 router_lifetime = 1; + + // Prefix valid lifetime from the prefix information option. + optional int64 prefix_valid_lifetime = 2; + + // Prefix preferred lifetime from the prefix information option. + optional int64 prefix_preferred_lifetime = 3; + + // Route info lifetime. + optional int64 route_info_lifetime = 4; + + // Recursive DNS server lifetime. + optional int64 rdnss_lifetime = 5; + + // DNS search list lifetime. + optional int64 dnssl_lifetime = 6; +} + +// Represents an IP provisioning event in IpManager and how long the +// provisioning action took. +// 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; + + // The code of the IP provisioning event, represented by constants defined in + // android.net.metrics.IpManagerEvent. + optional int32 event_type = 2; + + // The duration of the provisioning action that resulted in this event. + optional int32 latency_ms = 3; +} + +// Represents one of the IP connectivity event defined in this file. +// Next tag: 12 +message IpConnectivityEvent { + // Time in ms when the event was recorded. + optional int64 time_ms = 1; + + // Event type. + oneof event { + + // An event about the system default network. + DefaultNetworkEvent default_network_event = 2; + + // An IP reachability probe event. + IpReachabilityEvent ip_reachability_event = 3; + + // A network lifecycle event. + NetworkEvent network_event = 4; + + // A batch of DNS lookups. + DNSLookupBatch dns_lookup_batch = 5; + + // A DHCP client event or DHCP receive error. + DHCPEvent dhcp_event = 6; + + // An IP provisioning event. + IpProvisioningEvent ip_provisioning_event = 7; + + // A network validation probe event. + ValidationProbeEvent validation_probe_event = 8; + + // An Android Packet Filter program event. + ApfProgramEvent apf_program_event = 9; + + // An Android Packet Filter statistics event. + ApfStatistics apf_statistics = 10; + + // An RA packet reception event. + RaEvent ra_event = 11; + }; +}; + +// The information about IP connectivity events. +message IpConnectivityLog { + // An array of IP connectivity events. + repeated IpConnectivityEvent events = 1; + + // The number of events that had to be dropped due to a full buffer. + optional int32 dropped_events = 2; +}; diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java new file mode 100644 index 0000000000000..aed3635245607 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java @@ -0,0 +1,359 @@ +/* + * 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.net.ConnectivityMetricsEvent; +import android.net.metrics.ApfProgramEvent; +import android.net.metrics.ApfStats; +import android.net.metrics.DefaultNetworkEvent; +import android.net.metrics.DhcpClientEvent; +import android.net.metrics.DhcpErrorEvent; +import android.net.metrics.DnsEvent; +import android.net.metrics.IpManagerEvent; +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 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 { + + public void testDefaultNetworkEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(DefaultNetworkEvent.class), + anInt(102), + anIntArray(1, 2, 3), + anInt(101), + aBool(true), + aBool(false)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " default_network_event <", + " network_id <", + " network_id: 102", + " >", + " previous_network_id <", + " network_id: 101", + " >", + " previous_network_ip_support: 1", + " transport_types: 1", + " transport_types: 2", + " transport_types: 3", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testDhcpClientEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(DhcpClientEvent.class), + aString("wlan0"), + aString("SomeState"), + anInt(192)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " dhcp_event <", + " duration_ms: 192", + " error_code: 0", + " if_name: \"wlan0\"", + " state_transition: \"SomeState\"", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testDhcpErrorEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(DhcpErrorEvent.class), + aString("wlan0"), + anInt(DhcpErrorEvent.L4_NOT_UDP)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " dhcp_event <", + " duration_ms: 0", + " error_code: 50397184", + " if_name: \"wlan0\"", + " state_transition: \"\"", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testDnsEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(DnsEvent.class), + anInt(101), + aByteArray(b(1), b(1), b(2), b(1), b(1), b(1), b(2), b(2)), + aByteArray(b(0), b(0), b(22), b(3), b(1), b(0), b(200), b(178)), + anIntArray(3456, 267, 1230, 45, 2111, 450, 638, 1300)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " 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", + " network_id <", + " network_id: 101", + " >", + " return_codes: 0", + " return_codes: 0", + " return_codes: 22", + " return_codes: 3", + " return_codes: 1", + " return_codes: 0", + " return_codes: 200", + " return_codes: 178", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testIpManagerEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(IpManagerEvent.class), + aString("wlan0"), + anInt(IpManagerEvent.PROVISIONING_OK), + aLong(5678)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " ip_provisioning_event <", + " event_type: 1", + " if_name: \"wlan0\"", + " latency_ms: 5678", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testIpReachabilityEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(IpReachabilityEvent.class), + aString("wlan0"), + anInt(IpReachabilityEvent.NUD_FAILED)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"wlan0\"", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testNetworkEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(NetworkEvent.class), + anInt(100), + anInt(5), + aLong(20410)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " network_event <", + " event_type: 5", + " latency_ms: 20410", + " network_id <", + " network_id: 100", + " >", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testValidationProbeEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(ValidationProbeEvent.class), + anInt(120), + aLong(40730), + anInt(ValidationProbeEvent.PROBE_HTTP), + anInt(204)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " time_ms: 1", + " validation_probe_event <", + " latency_ms: 40730", + " network_id <", + " network_id: 120", + " >", + " probe_result: 204", + " probe_type: 1", + " >", + ">"); + + verifySerialization(want, ev); + } + + public void testApfProgramEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(ApfProgramEvent.class), + aLong(200), + anInt(7), + anInt(9), + anInt(2048), + anInt(3)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " apf_program_event <", + " current_ras: 9", + " drop_multicast: true", + " filtered_ras: 7", + " has_ipv4_addr: true", + " lifetime: 200", + " program_length: 2048", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testApfStatsSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(ApfStats.class), + aLong(45000), + anInt(10), + anInt(2), + anInt(2), + anInt(1), + anInt(2), + anInt(4), + anInt(2048)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " apf_statistics <", + " dropped_ras: 2", + " duration_ms: 45000", + " matching_ras: 2", + " max_program_size: 2048", + " parse_errors: 2", + " program_updates: 4", + " received_ras: 10", + " zero_lifetime_ras: 1", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + public void testRaEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(RaEvent.class), + aLong(2000), + aLong(400), + aLong(300), + aLong(-1), + aLong(1000), + aLong(-1)); + + String want = joinLines( + "dropped_events: 0", + "events <", + " ra_event <", + " dnssl_lifetime: -1", + " prefix_preferred_lifetime: 300", + " prefix_valid_lifetime: 400", + " rdnss_lifetime: 1000", + " route_info_lifetime: -1", + " router_lifetime: 2000", + " >", + " time_ms: 1", + ">"); + + verifySerialization(want, ev); + } + + static void verifySerialization(String want, ConnectivityMetricsEvent... input) { + try { + byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input)); + IpConnectivityLog log = new IpConnectivityLog(); + MessageNano.mergeFrom(log, got); + assertEquals(want, log.toString()); + } catch (Exception e) { + fail(e.toString()); + } + } + + static String joinLines(String ... elems) { + StringBuilder b = new StringBuilder(); + for (String s : elems) { + b.append(s); + b.append("\n"); + } + return b.toString(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/connectivity/MetricsTestUtil.java b/services/tests/servicestests/src/com/android/server/connectivity/MetricsTestUtil.java new file mode 100644 index 0000000000000..e201012781211 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/connectivity/MetricsTestUtil.java @@ -0,0 +1,120 @@ +/* + * 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.net.ConnectivityMetricsEvent; +import android.net.ConnectivityMetricsLogger; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +abstract public class MetricsTestUtil { + private MetricsTestUtil() { + } + + static ConnectivityMetricsEvent ipEv(Parcelable p) { + return ev(ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY, p); + } + + static ConnectivityMetricsEvent telephonyEv() { + return ev(ConnectivityMetricsLogger.COMPONENT_TAG_TELEPHONY, new Bundle()); + } + + static ConnectivityMetricsEvent ev(int tag, Parcelable p) { + return new ConnectivityMetricsEvent(1L, tag, 0, p); + } + + // Utiliy interface for describing the content of a Parcel. This relies on + // the implementation defails of Parcelable and on the fact that the fully + // qualified Parcelable class names are written as string in the Parcels. + interface ParcelField { + void write(Parcel p); + } + + static ConnectivityMetricsEvent describeIpEvent(ParcelField... fs) { + Parcel p = Parcel.obtain(); + for (ParcelField f : fs) { + f.write(p); + } + p.setDataPosition(0); + return ipEv(p.readParcelable(ClassLoader.getSystemClassLoader())); + } + + static ParcelField aType(Class c) { + return new ParcelField() { + public void write(Parcel p) { + p.writeString(c.getName()); + } + }; + } + + static ParcelField aBool(boolean b) { + return aByte((byte) (b ? 1 : 0)); + } + + static ParcelField aByte(byte b) { + return new ParcelField() { + public void write(Parcel p) { + p.writeByte(b); + } + }; + } + + static ParcelField anInt(int i) { + return new ParcelField() { + public void write(Parcel p) { + p.writeInt(i); + } + }; + } + + static ParcelField aLong(long l) { + return new ParcelField() { + public void write(Parcel p) { + p.writeLong(l); + } + }; + } + + static ParcelField aString(String s) { + return new ParcelField() { + public void write(Parcel p) { + p.writeString(s); + } + }; + } + + static ParcelField aByteArray(byte... ary) { + return new ParcelField() { + public void write(Parcel p) { + p.writeByteArray(ary); + } + }; + } + + static ParcelField anIntArray(int... ary) { + return new ParcelField() { + public void write(Parcel p) { + p.writeIntArray(ary); + } + }; + } + + static byte b(int i) { + return (byte) i; + } +}