1 /* 2 * Copyright (C) 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity; 18 19 import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; 20 import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; 21 import static org.mockito.Mockito.timeout; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertFalse; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 29 import android.content.Context; 30 import android.net.ConnectivityManager; 31 import android.net.ConnectivityMetricsEvent; 32 import android.net.IIpConnectivityMetrics; 33 import android.net.Network; 34 import android.net.NetworkCapabilities; 35 import android.net.metrics.ApfProgramEvent; 36 import android.net.metrics.ApfStats; 37 import android.net.metrics.DefaultNetworkEvent; 38 import android.net.metrics.DhcpClientEvent; 39 import android.net.metrics.IpConnectivityLog; 40 import android.net.metrics.IpManagerEvent; 41 import android.net.metrics.IpReachabilityEvent; 42 import android.net.metrics.RaEvent; 43 import android.net.metrics.ValidationProbeEvent; 44 import android.system.OsConstants; 45 import android.os.Parcelable; 46 import android.support.test.runner.AndroidJUnit4; 47 import android.test.suitebuilder.annotation.SmallTest; 48 import android.util.Base64; 49 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; 50 import java.io.PrintWriter; 51 import java.io.StringWriter; 52 import java.util.Collections; 53 import java.util.Comparator; 54 import java.util.Iterator; 55 import java.util.List; 56 import org.mockito.ArgumentCaptor; 57 import org.mockito.Mock; 58 import org.mockito.MockitoAnnotations; 59 import org.junit.Before; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 63 @RunWith(AndroidJUnit4.class) 64 @SmallTest 65 public class IpConnectivityMetricsTest { 66 static final IpReachabilityEvent FAKE_EV = 67 new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); 68 69 private static final String EXAMPLE_IPV4 = "192.0.2.1"; 70 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; 71 72 @Mock Context mCtx; 73 @Mock IIpConnectivityMetrics mMockService; 74 @Mock ConnectivityManager mCm; 75 76 IpConnectivityMetrics mService; 77 NetdEventListenerService mNetdListener; 78 79 @Before setUp()80 public void setUp() { 81 MockitoAnnotations.initMocks(this); 82 mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000); 83 mNetdListener = new NetdEventListenerService(mCm); 84 mService.mNetdListener = mNetdListener; 85 } 86 87 @Test testLoggingEvents()88 public void testLoggingEvents() throws Exception { 89 IpConnectivityLog logger = new IpConnectivityLog(mMockService); 90 91 assertTrue(logger.log(1, FAKE_EV)); 92 assertTrue(logger.log(2, FAKE_EV)); 93 assertTrue(logger.log(3, FAKE_EV)); 94 95 List<ConnectivityMetricsEvent> got = verifyEvents(3); 96 assertEventsEqual(expectedEvent(1), got.get(0)); 97 assertEventsEqual(expectedEvent(2), got.get(1)); 98 assertEventsEqual(expectedEvent(3), got.get(2)); 99 } 100 101 @Test testLoggingEventsWithMultipleCallers()102 public void testLoggingEventsWithMultipleCallers() throws Exception { 103 IpConnectivityLog logger = new IpConnectivityLog(mMockService); 104 105 final int nCallers = 10; 106 final int nEvents = 10; 107 for (int n = 0; n < nCallers; n++) { 108 final int i = n; 109 new Thread() { 110 public void run() { 111 for (int j = 0; j < nEvents; j++) { 112 assertTrue(logger.log(1 + i * 100 + j, FAKE_EV)); 113 } 114 } 115 }.start(); 116 } 117 118 List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200); 119 Collections.sort(got, EVENT_COMPARATOR); 120 Iterator<ConnectivityMetricsEvent> iter = got.iterator(); 121 for (int i = 0; i < nCallers; i++) { 122 for (int j = 0; j < nEvents; j++) { 123 int expectedTimestamp = 1 + i * 100 + j; 124 assertEventsEqual(expectedEvent(expectedTimestamp), iter.next()); 125 } 126 } 127 } 128 129 @Test testBufferFlushing()130 public void testBufferFlushing() { 131 String output1 = getdump("flush"); 132 assertEquals("", output1); 133 134 new IpConnectivityLog(mService.impl).log(1, FAKE_EV); 135 String output2 = getdump("flush"); 136 assertFalse("".equals(output2)); 137 138 String output3 = getdump("flush"); 139 assertEquals("", output3); 140 } 141 142 @Test testRateLimiting()143 public void testRateLimiting() { 144 final IpConnectivityLog logger = new IpConnectivityLog(mService.impl); 145 final ApfProgramEvent ev = new ApfProgramEvent(); 146 final long fakeTimestamp = 1; 147 148 int attempt = 100; // More than burst quota, but less than buffer size. 149 for (int i = 0; i < attempt; i++) { 150 logger.log(ev); 151 } 152 153 String output1 = getdump("flush"); 154 assertFalse("".equals(output1)); 155 156 for (int i = 0; i < attempt; i++) { 157 assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev)); 158 } 159 160 String output2 = getdump("flush"); 161 assertEquals("", output2); 162 } 163 164 @Test testEndToEndLogging()165 public void testEndToEndLogging() throws Exception { 166 // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. 167 IpConnectivityLog logger = new IpConnectivityLog(mService.impl); 168 169 NetworkCapabilities ncWifi = new NetworkCapabilities(); 170 NetworkCapabilities ncCell = new NetworkCapabilities(); 171 ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); 172 ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 173 174 when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); 175 when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); 176 177 ApfStats apfStats = new ApfStats(); 178 apfStats.durationMs = 45000; 179 apfStats.receivedRas = 10; 180 apfStats.matchingRas = 2; 181 apfStats.droppedRas = 2; 182 apfStats.parseErrors = 2; 183 apfStats.zeroLifetimeRas = 1; 184 apfStats.programUpdates = 4; 185 apfStats.programUpdatesAll = 7; 186 apfStats.programUpdatesAllowingMulticast = 3; 187 apfStats.maxProgramSize = 2048; 188 189 ValidationProbeEvent validationEv = new ValidationProbeEvent(); 190 validationEv.durationMs = 40730; 191 validationEv.probeType = ValidationProbeEvent.PROBE_HTTP; 192 validationEv.returnCode = 204; 193 194 Parcelable[] events = { 195 new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), 196 new DhcpClientEvent("SomeState", 192), 197 new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false), 198 new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678), 199 validationEv, 200 apfStats, 201 new RaEvent(2000, 400, 300, -1, 1000, -1) 202 }; 203 204 for (int i = 0; i < events.length; i++) { 205 ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); 206 ev.timestamp = 100 * (i + 1); 207 ev.ifname = "wlan0"; 208 ev.data = events[i]; 209 logger.log(ev); 210 } 211 212 // netId, errno, latency, destination 213 connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4); 214 connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6); 215 connectEvent(100, 0, 110, EXAMPLE_IPV4); 216 connectEvent(101, 0, 23, EXAMPLE_IPV4); 217 connectEvent(101, 0, 45, EXAMPLE_IPV6); 218 connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4); 219 220 // netId, type, return code, latency 221 dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); 222 dnsEvent(100, EVENT_GETADDRINFO, 3, 45); 223 dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638); 224 dnsEvent(101, EVENT_GETADDRINFO, 0, 56); 225 dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34); 226 227 String want = String.join("\n", 228 "dropped_events: 0", 229 "events <", 230 " if_name: \"\"", 231 " link_layer: 4", 232 " network_id: 0", 233 " time_ms: 100", 234 " transports: 0", 235 " ip_reachability_event <", 236 " event_type: 512", 237 " if_name: \"\"", 238 " >", 239 ">", 240 "events <", 241 " if_name: \"\"", 242 " link_layer: 4", 243 " network_id: 0", 244 " time_ms: 200", 245 " transports: 0", 246 " dhcp_event <", 247 " duration_ms: 192", 248 " if_name: \"\"", 249 " state_transition: \"SomeState\"", 250 " >", 251 ">", 252 "events <", 253 " if_name: \"\"", 254 " link_layer: 4", 255 " network_id: 0", 256 " time_ms: 300", 257 " transports: 0", 258 " default_network_event <", 259 " network_id <", 260 " network_id: 102", 261 " >", 262 " previous_network_id <", 263 " network_id: 101", 264 " >", 265 " previous_network_ip_support: 1", 266 " transport_types: 1", 267 " transport_types: 2", 268 " transport_types: 3", 269 " >", 270 ">", 271 "events <", 272 " if_name: \"\"", 273 " link_layer: 4", 274 " network_id: 0", 275 " time_ms: 400", 276 " transports: 0", 277 " ip_provisioning_event <", 278 " event_type: 1", 279 " if_name: \"\"", 280 " latency_ms: 5678", 281 " >", 282 ">", 283 "events <", 284 " if_name: \"\"", 285 " link_layer: 4", 286 " network_id: 0", 287 " time_ms: 500", 288 " transports: 0", 289 " validation_probe_event <", 290 " latency_ms: 40730", 291 " probe_result: 204", 292 " probe_type: 1", 293 " >", 294 ">", 295 "events <", 296 " if_name: \"\"", 297 " link_layer: 4", 298 " network_id: 0", 299 " time_ms: 600", 300 " transports: 0", 301 " apf_statistics <", 302 " dropped_ras: 2", 303 " duration_ms: 45000", 304 " matching_ras: 2", 305 " max_program_size: 2048", 306 " parse_errors: 2", 307 " program_updates: 4", 308 " program_updates_all: 7", 309 " program_updates_allowing_multicast: 3", 310 " received_ras: 10", 311 " zero_lifetime_ras: 1", 312 " >", 313 ">", 314 "events <", 315 " if_name: \"\"", 316 " link_layer: 4", 317 " network_id: 0", 318 " time_ms: 700", 319 " transports: 0", 320 " ra_event <", 321 " dnssl_lifetime: -1", 322 " prefix_preferred_lifetime: 300", 323 " prefix_valid_lifetime: 400", 324 " rdnss_lifetime: 1000", 325 " route_info_lifetime: -1", 326 " router_lifetime: 2000", 327 " >", 328 ">", 329 "events <", 330 " if_name: \"\"", 331 " link_layer: 4", 332 " network_id: 100", 333 " time_ms: 0", 334 " transports: 2", 335 " connect_statistics <", 336 " connect_blocking_count: 1", 337 " connect_count: 3", 338 " errnos_counters <", 339 " key: 11", 340 " value: 1", 341 " >", 342 " ipv6_addr_count: 1", 343 " latencies_ms: 110", 344 " >", 345 ">", 346 "events <", 347 " if_name: \"\"", 348 " link_layer: 2", 349 " network_id: 101", 350 " time_ms: 0", 351 " transports: 1", 352 " connect_statistics <", 353 " connect_blocking_count: 2", 354 " connect_count: 2", 355 " ipv6_addr_count: 1", 356 " latencies_ms: 23", 357 " latencies_ms: 45", 358 " >", 359 ">", 360 "events <", 361 " if_name: \"\"", 362 " link_layer: 4", 363 " network_id: 100", 364 " time_ms: 0", 365 " transports: 2", 366 " dns_lookup_batch <", 367 " event_types: 1", 368 " event_types: 1", 369 " event_types: 2", 370 " latencies_ms: 3456", 371 " latencies_ms: 45", 372 " latencies_ms: 638", 373 " return_codes: 0", 374 " return_codes: 3", 375 " return_codes: 0", 376 " >", 377 ">", 378 "events <", 379 " if_name: \"\"", 380 " link_layer: 2", 381 " network_id: 101", 382 " time_ms: 0", 383 " transports: 1", 384 " dns_lookup_batch <", 385 " event_types: 1", 386 " event_types: 2", 387 " latencies_ms: 56", 388 " latencies_ms: 34", 389 " return_codes: 0", 390 " return_codes: 0", 391 " >", 392 ">", 393 "version: 2\n"); 394 395 verifySerialization(want, getdump("flush")); 396 } 397 getdump(String .... command)398 String getdump(String ... command) { 399 StringWriter buffer = new StringWriter(); 400 PrintWriter writer = new PrintWriter(buffer); 401 mService.impl.dump(null, writer, command); 402 return buffer.toString(); 403 } 404 connectEvent(int netid, int error, int latencyMs, String ipAddr)405 void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception { 406 mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1); 407 } 408 dnsEvent(int netId, int type, int result, int latency)409 void dnsEvent(int netId, int type, int result, int latency) throws Exception { 410 mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); 411 } 412 verifyEvents(int n, int timeoutMs)413 List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception { 414 ArgumentCaptor<ConnectivityMetricsEvent> captor = 415 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); 416 verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture()); 417 return captor.getAllValues(); 418 } 419 verifyEvents(int n)420 List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception { 421 return verifyEvents(n, 10); 422 } 423 verifySerialization(String want, String output)424 static void verifySerialization(String want, String output) { 425 try { 426 byte[] got = Base64.decode(output, Base64.DEFAULT); 427 IpConnectivityLogClass.IpConnectivityLog log = 428 IpConnectivityLogClass.IpConnectivityLog.parseFrom(got); 429 assertEquals(want, log.toString()); 430 } catch (Exception e) { 431 fail(e.toString()); 432 } 433 } 434 joinLines(String .... elems)435 static String joinLines(String ... elems) { 436 StringBuilder b = new StringBuilder(); 437 for (String s : elems) { 438 b.append(s).append("\n"); 439 } 440 return b.toString(); 441 } 442 expectedEvent(int timestamp)443 static ConnectivityMetricsEvent expectedEvent(int timestamp) { 444 ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); 445 ev.timestamp = timestamp; 446 ev.data = FAKE_EV; 447 return ev; 448 } 449 450 /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got)451 static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) { 452 assertEquals(expected.timestamp, got.timestamp); 453 assertEquals(expected.data, got.data); 454 } 455 456 static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR = 457 Comparator.comparingLong((ev) -> ev.timestamp); 458 } 459