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";
}
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];
- }
- };
}
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);
}
}
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;
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;
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;
}
@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);
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,
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 <",
" 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: \"\"",
" if_name: \"\"",
" link_layer: 4",
" network_id: 0",
- " time_ms: 500",
+ " time_ms: 400",
" transports: 0",
" validation_probe_event <",
" latency_ms: 40730",
" if_name: \"\"",
" link_layer: 4",
" network_id: 0",
- " time_ms: 600",
+ " time_ms: 500",
" transports: 0",
" apf_statistics <",
" dropped_ras: 2",
" if_name: \"\"",
" link_layer: 4",
" network_id: 0",
- " time_ms: 700",
+ " time_ms: 600",
" transports: 0",
" ra_event <",
" dnssl_lifetime: -1",
">",
"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",
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);