diff options
6 files changed, 308 insertions, 130 deletions
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java index 28cf42f2fe28..eb61c1532d49 100644 --- a/core/java/android/net/metrics/DefaultNetworkEvent.java +++ b/core/java/android/net/metrics/DefaultNetworkEvent.java @@ -16,73 +16,43 @@ package android.net.metrics; +import static android.net.ConnectivityManager.NETID_UNSET; + import android.net.NetworkCapabilities; -import android.os.Parcel; -import android.os.Parcelable; /** * An event recorded by ConnectivityService when there is a change in the default network. * {@hide} */ -public final class DefaultNetworkEvent implements Parcelable { +public class DefaultNetworkEvent { + // The ID of the network that has become the new default or NETID_UNSET if none. - public final int netId; + public int netId = NETID_UNSET; // The list of transport types of the new default network, for example TRANSPORT_WIFI, as // defined in NetworkCapabilities.java. - public final int[] transportTypes; + public int[] transportTypes = new int[0]; // The ID of the network that was the default before or NETID_UNSET if none. - public final int prevNetId; + public int prevNetId = NETID_UNSET; // Whether the previous network had IPv4/IPv6 connectivity. - public final boolean prevIPv4; - public final boolean prevIPv6; - - public DefaultNetworkEvent(int netId, int[] transportTypes, - int prevNetId, boolean prevIPv4, boolean prevIPv6) { - this.netId = netId; - this.transportTypes = transportTypes; - this.prevNetId = prevNetId; - this.prevIPv4 = prevIPv4; - this.prevIPv6 = prevIPv6; - } - - private DefaultNetworkEvent(Parcel in) { - this.netId = in.readInt(); - this.transportTypes = in.createIntArray(); - this.prevNetId = in.readInt(); - this.prevIPv4 = (in.readByte() > 0); - this.prevIPv6 = (in.readByte() > 0); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(netId); - out.writeIntArray(transportTypes); - out.writeInt(prevNetId); - out.writeByte(prevIPv4 ? (byte) 1 : (byte) 0); - out.writeByte(prevIPv6 ? (byte) 1 : (byte) 0); - } - - @Override - public int describeContents() { - return 0; - } + public boolean prevIPv4; + public boolean prevIPv6; @Override public String toString() { - String prevNetwork = String.valueOf(prevNetId); - String newNetwork = String.valueOf(netId); - if (prevNetId != 0) { - prevNetwork += ":" + ipSupport(); - } - if (netId != 0) { - newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes); - } - return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork); + String prevNetwork = String.valueOf(prevNetId); + String newNetwork = String.valueOf(netId); + if (prevNetId != 0) { + prevNetwork += ":" + ipSupport(); + } + if (netId != 0) { + newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes); + } + return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork); } private String ipSupport() { if (prevIPv4 && prevIPv6) { - return "DUAL"; + return "IPv4v6"; } if (prevIPv6) { return "IPv6"; @@ -92,15 +62,4 @@ public final class DefaultNetworkEvent implements Parcelable { } return "NONE"; } - - public static final Parcelable.Creator<DefaultNetworkEvent> CREATOR - = new Parcelable.Creator<DefaultNetworkEvent>() { - public DefaultNetworkEvent createFromParcel(Parcel in) { - return new DefaultNetworkEvent(in); - } - - public DefaultNetworkEvent[] newArray(int size) { - return new DefaultNetworkEvent[size]; - } - }; } diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java index c7c00bb3b670..8981db11f7f8 100644 --- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java +++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java @@ -16,38 +16,63 @@ package com.android.server.connectivity; -import static android.net.ConnectivityManager.NETID_UNSET; - import android.net.LinkProperties; import android.net.metrics.DefaultNetworkEvent; import android.net.metrics.IpConnectivityLog; +import com.android.internal.annotations.GuardedBy; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + /** * Tracks events related to the default network for the purpose of default network metrics. * {@hide} */ public class DefaultNetworkMetrics { - private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); + private static final int ROLLING_LOG_SIZE = 64; + + // Event buffer used for metrics upload. The buffer is cleared when events are collected. + @GuardedBy("this") + private final List<DefaultNetworkEvent> mEvents = new ArrayList<>(); + + public synchronized void listEvents(PrintWriter pw) { + long localTimeMs = System.currentTimeMillis(); + for (DefaultNetworkEvent ev : mEvents) { + pw.println(ev); + } + } + + public synchronized void listEventsAsProto(PrintWriter pw) { + for (DefaultNetworkEvent ev : mEvents) { + pw.print(IpConnectivityEventBuilder.toProto(ev)); + } + } - public void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) { - int newNetid = NETID_UNSET; - int prevNetid = NETID_UNSET; - int[] transports = new int[0]; - boolean hadIPv4 = false; - boolean hadIPv6 = false; + public synchronized void flushEvents(List<IpConnectivityEvent> out) { + for (DefaultNetworkEvent ev : mEvents) { + out.add(IpConnectivityEventBuilder.toProto(ev)); + } + mEvents.clear(); + } + public synchronized void logDefaultNetworkEvent( + NetworkAgentInfo newNai, NetworkAgentInfo prevNai) { + DefaultNetworkEvent ev = new DefaultNetworkEvent(); if (newNai != null) { - newNetid = newNai.network.netId; - transports = newNai.networkCapabilities.getTransportTypes(); + ev.netId = newNai.network().netId; + ev.transportTypes = newNai.networkCapabilities.getTransportTypes(); } if (prevNai != null) { - prevNetid = prevNai.network.netId; + ev.prevNetId = prevNai.network().netId; final LinkProperties lp = prevNai.linkProperties; - hadIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute(); - hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute(); + ev.prevIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute(); + ev.prevIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute(); } - mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6)); + mEvents.add(ev); } } diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java index 67e72167faa7..3d71ecb50d9f 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java @@ -132,6 +132,18 @@ final public class IpConnectivityEventBuilder { return out; } + public static IpConnectivityEvent toProto(DefaultNetworkEvent in) { + IpConnectivityLogClass.DefaultNetworkEvent ev = + new IpConnectivityLogClass.DefaultNetworkEvent(); + ev.networkId = netIdOf(in.netId); + ev.previousNetworkId = netIdOf(in.prevNetId); + ev.transportTypes = in.transportTypes; + ev.previousNetworkIpSupport = ipSupportOf(in); + final IpConnectivityEvent out = buildEvent(in.netId, 0, null); + out.setDefaultNetworkEvent(ev); + return out; + } + private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) { final IpConnectivityEvent ev = new IpConnectivityEvent(); ev.networkId = netId; @@ -164,11 +176,6 @@ final public class IpConnectivityEventBuilder { return true; } - if (in instanceof DefaultNetworkEvent) { - setDefaultNetworkEvent(out, (DefaultNetworkEvent) in); - return true; - } - if (in instanceof NetworkEvent) { setNetworkEvent(out, (NetworkEvent) in); return true; @@ -225,16 +232,6 @@ final public class IpConnectivityEventBuilder { out.setIpReachabilityEvent(ipReachabilityEvent); } - private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) { - IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent = - new IpConnectivityLogClass.DefaultNetworkEvent(); - defaultNetworkEvent.networkId = netIdOf(in.netId); - defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId); - defaultNetworkEvent.transportTypes = in.transportTypes; - defaultNetworkEvent.previousNetworkIpSupport = ipSupportOf(in); - out.setDefaultNetworkEvent(defaultNetworkEvent); - } - private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) { IpConnectivityLogClass.NetworkEvent networkEvent = new IpConnectivityLogClass.NetworkEvent(); diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java index 25e541f17baf..24217e6eef0b 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -196,6 +196,8 @@ final public class IpConnectivityMetrics extends SystemService { final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events); + mDefaultNetworkMetrics.flushEvents(protoEvents); + if (mNetdListener != null) { mNetdListener.flushStatistics(protoEvents); } @@ -236,6 +238,7 @@ final public class IpConnectivityMetrics extends SystemService { if (mNetdListener != null) { mNetdListener.listAsProtos(pw); } + mDefaultNetworkMetrics.listEventsAsProto(pw); return; } @@ -245,6 +248,7 @@ final public class IpConnectivityMetrics extends SystemService { if (mNetdListener != null) { mNetdListener.list(pw); } + mDefaultNetworkMetrics.listEvents(pw); } /** @@ -262,6 +266,7 @@ final public class IpConnectivityMetrics extends SystemService { if (mNetdListener != null) { mNetdListener.list(pw); } + mDefaultNetworkMetrics.listEvents(pw); } private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java index 262417620ca2..ad6ebf933776 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java @@ -198,21 +198,20 @@ public class IpConnectivityEventBuilderTest { @Test public void testDefaultNetworkEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(DefaultNetworkEvent.class), - anInt(102), - anIntArray(1, 2, 3), - anInt(101), - aBool(true), - aBool(false)); + DefaultNetworkEvent ev = new DefaultNetworkEvent(); + ev.netId = 102; + ev.prevNetId = 101; + ev.transportTypes = new int[]{1, 2, 3}; + ev.prevIPv4 = true; + ev.prevIPv6 = true; String want = String.join("\n", "dropped_events: 0", "events <", " if_name: \"\"", " link_layer: 0", - " network_id: 0", - " time_ms: 1", + " network_id: 102", + " time_ms: 0", " transports: 0", " default_network_event <", " default_network_duration_ms: 0", @@ -226,7 +225,7 @@ public class IpConnectivityEventBuilderTest { " previous_network_id <", " network_id: 101", " >", - " previous_network_ip_support: 1", + " previous_network_ip_support: 3", " transport_types: 1", " transport_types: 2", " transport_types: 3", @@ -234,7 +233,7 @@ public class IpConnectivityEventBuilderTest { ">", "version: 2\n"); - verifySerialization(want, ev); + verifySerialization(want, IpConnectivityEventBuilder.toProto(ev)); } @Test diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index a395c480f57a..6c1decc3b3b8 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -18,6 +18,7 @@ package com.android.server.connectivity; import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,6 +31,10 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; import android.net.Network; import android.net.NetworkCapabilities; import android.net.metrics.ApfProgramEvent; @@ -41,18 +46,22 @@ import android.net.metrics.IpManagerEvent; import android.net.metrics.IpReachabilityEvent; import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; -import android.system.OsConstants; import android.os.Parcelable; import android.support.test.runner.AndroidJUnit4; +import android.system.OsConstants; import android.test.suitebuilder.annotation.SmallTest; import android.util.Base64; + +import com.android.internal.util.BitUtils; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; + import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; + import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -162,6 +171,144 @@ public class IpConnectivityMetricsTest { } @Test + public void testDefaultNetworkEvents() throws Exception { + final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); + final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkAgentInfo[][] defaultNetworks = { + // nothing -> cell + {null, makeNai(100, 10, false, true, cell)}, + // cell -> wifi + {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)}, + // wifi -> nothing + {makeNai(101, 60, true, false, wifi), null}, + // nothing -> cell + {null, makeNai(102, 10, true, true, cell)}, + // cell -> wifi + {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)}, + }; + + for (NetworkAgentInfo[] pair : defaultNetworks) { + mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]); + } + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 100", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 100", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 0", + " >", + " previous_network_ip_support: 0", + " transport_types: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 101", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 101", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 100", + " >", + " previous_network_ip_support: 3", + " transport_types: 1", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 0", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 101", + " >", + " previous_network_ip_support: 1", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 102", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 102", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 0", + " >", + " previous_network_ip_support: 0", + " transport_types: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 103", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 103", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 102", + " >", + " previous_network_ip_support: 3", + " transport_types: 1", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, getdump("flush")); + } + + @Test public void testEndToEndLogging() throws Exception { // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. IpConnectivityLog logger = new IpConnectivityLog(mService.impl); @@ -194,7 +341,6 @@ public class IpConnectivityMetricsTest { Parcelable[] events = { new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), new DhcpClientEvent("SomeState", 192), - new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false), new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678), validationEv, apfStats, @@ -233,6 +379,13 @@ public class IpConnectivityMetricsTest { wakeupEvent("wlan0", 10008); wakeupEvent("rmnet0", 1000); + final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); + final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell); + NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi); + mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null); + mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai); + String want = String.join("\n", "dropped_events: 0", "events <", @@ -264,30 +417,6 @@ public class IpConnectivityMetricsTest { " network_id: 0", " time_ms: 300", " transports: 0", - " default_network_event <", - " default_network_duration_ms: 0", - " final_score: 0", - " initial_score: 0", - " ip_support: 0", - " network_id <", - " network_id: 102", - " >", - " no_default_network_duration_ms: 0", - " previous_network_id <", - " network_id: 101", - " >", - " previous_network_ip_support: 1", - " transport_types: 1", - " transport_types: 2", - " transport_types: 3", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 400", - " transports: 0", " ip_provisioning_event <", " event_type: 1", " if_name: \"\"", @@ -298,7 +427,7 @@ public class IpConnectivityMetricsTest { " if_name: \"\"", " link_layer: 4", " network_id: 0", - " time_ms: 500", + " time_ms: 400", " transports: 0", " validation_probe_event <", " latency_ms: 40730", @@ -310,7 +439,7 @@ public class IpConnectivityMetricsTest { " if_name: \"\"", " link_layer: 4", " network_id: 0", - " time_ms: 600", + " time_ms: 500", " transports: 0", " apf_statistics <", " dropped_ras: 2", @@ -331,7 +460,7 @@ public class IpConnectivityMetricsTest { " if_name: \"\"", " link_layer: 4", " network_id: 0", - " time_ms: 700", + " time_ms: 600", " transports: 0", " ra_event <", " dnssl_lifetime: -1", @@ -344,6 +473,50 @@ public class IpConnectivityMetricsTest { ">", "events <", " if_name: \"\"", + " link_layer: 0", + " network_id: 100", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 100", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 0", + " >", + " previous_network_ip_support: 0", + " transport_types: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 101", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 0", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " network_id <", + " network_id: 101", + " >", + " no_default_network_duration_ms: 0", + " previous_network_id <", + " network_id: 100", + " >", + " previous_network_ip_support: 2", + " transport_types: 1", + " >", + ">", + "events <", + " if_name: \"\"", " link_layer: 4", " network_id: 100", " time_ms: 0", @@ -471,6 +644,26 @@ public class IpConnectivityMetricsTest { mNetdListener.onWakeupEvent(prefix, uid, uid, 0); } + NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) { + NetworkAgentInfo nai = mock(NetworkAgentInfo.class); + when(nai.network()).thenReturn(new Network(netId)); + when(nai.getCurrentScore()).thenReturn(score); + nai.linkProperties = new LinkProperties(); + nai.networkCapabilities = new NetworkCapabilities(); + for (int t : BitUtils.unpackBits(transports)) { + nai.networkCapabilities.addTransportType(t); + } + if (ipv4) { + nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24")); + nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"))); + } + if (ipv6) { + nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64")); + nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0"))); + } + return nai; + } + List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception { ArgumentCaptor<ConnectivityMetricsEvent> captor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); |