From cd8d58c4052ad8a5af83ab1cf55cacebac73afc7 Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Thu, 19 Oct 2017 14:42:40 +0900 Subject: [PATCH 1/2] Extract logging of default network events This patch extracts the logging of DefaultNetworkEvent from inside ConnectivityService and move it to a new DefaultNetworkMetrics class. The DefaultNetworkMetrics is a singleton owned by the IpConnectivityMetrics singleton implementing the metrics service for core networking. ConnectivityService has access to this singleton via LocalServices. This class layout will allow to remove the Parcelable interface of DefaultNetworkEvent and will instead let the IpConnectivityMetrics service grab metrics from the DefaultNetworkMetrics directly. Bug: 34901696 Test: runtest frameworks-net Change-Id: I55694d89124272732aba114198776462372de18b --- .../android/server/ConnectivityService.java | 30 +++++-------------- .../server/ConnectivityServiceTest.java | 13 ++++++++ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 5c11100f34..25c96d14e9 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -71,7 +71,6 @@ import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.UidRange; import android.net.Uri; -import android.net.metrics.DefaultNetworkEvent; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.util.MultinetworkPolicyTracker; @@ -129,6 +128,7 @@ import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; +import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; @@ -2283,7 +2283,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Let rematchAllNetworksAndRequests() below record a new default network event // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence // whose timestamps tell how long it takes to recover a default network. - logDefaultNetworkEvent(null, nai); + metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai); } notifyIfacesChangedForNetworkStats(); // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied @@ -5012,7 +5012,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Notify system services that this network is up. makeDefault(newNetwork); // Log 0 -> X and Y -> X default network transitions, where X is the new default. - logDefaultNetworkEvent(newNetwork, oldDefaultNetwork); + metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent( + newNetwork, oldDefaultNetwork); // Have a new default network, release the transition wakelock in scheduleReleaseNetworkTransitionWakelock(); } @@ -5571,25 +5572,10 @@ public class ConnectivityService extends IConnectivityManager.Stub return ServiceManager.checkService(name) != null; } - private 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; - - if (newNai != null) { - newNetid = newNai.network.netId; - transports = newNai.networkCapabilities.getTransportTypes(); - } - if (prevNai != null) { - prevNetid = prevNai.network.netId; - final LinkProperties lp = prevNai.linkProperties; - hadIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute(); - hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute(); - } - - mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6)); + @VisibleForTesting + protected IpConnectivityMetrics.Logger metricsLogger() { + return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class), + "no IpConnectivityMetrics service"); } private void logNetworkEvent(NetworkAgentInfo nai, int evtype) { diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index c2cb66d5e6..27a29b68e1 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -104,6 +104,8 @@ import android.util.LogPrinter; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.connectivity.DefaultNetworkMetrics; +import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; @@ -156,6 +158,9 @@ public class ConnectivityServiceTest { private MockNetworkAgent mEthernetNetworkAgent; private Context mContext; + @Mock IpConnectivityMetrics.Logger mMetricsService; + @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; + // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods // do not go through ConnectivityService but talk to netd directly, so they don't automatically // reflect the state of our test ConnectivityService. @@ -805,6 +810,11 @@ public class ConnectivityServiceTest { return Context.ETHERNET_SERVICE.equals(name); } + @Override + protected IpConnectivityMetrics.Logger metricsLogger() { + return mMetricsService; + } + public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() { return mLastCreatedNetworkMonitor; } @@ -833,6 +843,9 @@ public class ConnectivityServiceTest { public void setUp() throws Exception { mContext = InstrumentationRegistry.getContext(); + MockitoAnnotations.initMocks(this); + when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics); + // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. // http://b/25897652 . if (Looper.myLooper() == null) { From 7e86f2e10ff2b6e4265da8b9eeaaeed32b82e66f Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Thu, 19 Oct 2017 14:58:15 +0900 Subject: [PATCH 2/2] Remove Parcelable interface from DefaultNetworkEvent This patch takes advantage of the direct DefaultNetworkMetrics interface between ConnectivityService and IpConnectivityMetrics and removes the Parcelable interface from DefaultNetworkEvent. IpConnectivityMetrics, IpConnectivityEventBuilder and associated tests are updated as necessary. Bug: 34901696 Test: runtest frameworks-net Change-Id: I59b6e04fc126051320d08a422cfbd4d27042123e --- .../IpConnectivityEventBuilderTest.java | 21 +- .../IpConnectivityMetricsTest.java | 251 ++++++++++++++++-- 2 files changed, 232 insertions(+), 40 deletions(-) diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java index 262417620c..ad6ebf9337 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 a395c480f5..6c1decc3b3 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; @@ -161,6 +170,144 @@ public class IpConnectivityMetricsTest { assertEquals("", output2); } + @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. @@ -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 verifyEvents(int n, int timeoutMs) throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);