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 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 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 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 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 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 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 // iface, uid 228 wakeupEvent("wlan0", 1000); 229 wakeupEvent("rmnet0", 10123); 230 wakeupEvent("wlan0", 1000); 231 wakeupEvent("rmnet0", 10008); 232 wakeupEvent("wlan0", -1); 233 wakeupEvent("wlan0", 10008); 234 wakeupEvent("rmnet0", 1000); 235 236 String want = String.join("\n", 237 "dropped_events: 0", 238 "events <", 239 " if_name: \"\"", 240 " link_layer: 4", 241 " network_id: 0", 242 " time_ms: 100", 243 " transports: 0", 244 " ip_reachability_event <", 245 " event_type: 512", 246 " if_name: \"\"", 247 " >", 248 ">", 249 "events <", 250 " if_name: \"\"", 251 " link_layer: 4", 252 " network_id: 0", 253 " time_ms: 200", 254 " transports: 0", 255 " dhcp_event <", 256 " duration_ms: 192", 257 " if_name: \"\"", 258 " state_transition: \"SomeState\"", 259 " >", 260 ">", 261 "events <", 262 " if_name: \"\"", 263 " link_layer: 4", 264 " network_id: 0", 265 " time_ms: 300", 266 " transports: 0", 267 " default_network_event <", 268 " default_network_duration_ms: 0", 269 " final_score: 0", 270 " initial_score: 0", 271 " ip_support: 0", 272 " network_id <", 273 " network_id: 102", 274 " >", 275 " no_default_network_duration_ms: 0", 276 " previous_network_id <", 277 " network_id: 101", 278 " >", 279 " previous_network_ip_support: 1", 280 " transport_types: 1", 281 " transport_types: 2", 282 " transport_types: 3", 283 " >", 284 ">", 285 "events <", 286 " if_name: \"\"", 287 " link_layer: 4", 288 " network_id: 0", 289 " time_ms: 400", 290 " transports: 0", 291 " ip_provisioning_event <", 292 " event_type: 1", 293 " if_name: \"\"", 294 " latency_ms: 5678", 295 " >", 296 ">", 297 "events <", 298 " if_name: \"\"", 299 " link_layer: 4", 300 " network_id: 0", 301 " time_ms: 500", 302 " transports: 0", 303 " validation_probe_event <", 304 " latency_ms: 40730", 305 " probe_result: 204", 306 " probe_type: 1", 307 " >", 308 ">", 309 "events <", 310 " if_name: \"\"", 311 " link_layer: 4", 312 " network_id: 0", 313 " time_ms: 600", 314 " transports: 0", 315 " apf_statistics <", 316 " dropped_ras: 2", 317 " duration_ms: 45000", 318 " matching_ras: 2", 319 " max_program_size: 2048", 320 " parse_errors: 2", 321 " program_updates: 4", 322 " program_updates_all: 7", 323 " program_updates_allowing_multicast: 3", 324 " received_ras: 10", 325 " total_packet_dropped: 0", 326 " total_packet_processed: 0", 327 " zero_lifetime_ras: 1", 328 " >", 329 ">", 330 "events <", 331 " if_name: \"\"", 332 " link_layer: 4", 333 " network_id: 0", 334 " time_ms: 700", 335 " transports: 0", 336 " ra_event <", 337 " dnssl_lifetime: -1", 338 " prefix_preferred_lifetime: 300", 339 " prefix_valid_lifetime: 400", 340 " rdnss_lifetime: 1000", 341 " route_info_lifetime: -1", 342 " router_lifetime: 2000", 343 " >", 344 ">", 345 "events <", 346 " if_name: \"\"", 347 " link_layer: 4", 348 " network_id: 100", 349 " time_ms: 0", 350 " transports: 2", 351 " connect_statistics <", 352 " connect_blocking_count: 1", 353 " connect_count: 3", 354 " errnos_counters <", 355 " key: 11", 356 " value: 1", 357 " >", 358 " ipv6_addr_count: 1", 359 " latencies_ms: 110", 360 " >", 361 ">", 362 "events <", 363 " if_name: \"\"", 364 " link_layer: 2", 365 " network_id: 101", 366 " time_ms: 0", 367 " transports: 1", 368 " connect_statistics <", 369 " connect_blocking_count: 2", 370 " connect_count: 2", 371 " ipv6_addr_count: 1", 372 " latencies_ms: 23", 373 " latencies_ms: 45", 374 " >", 375 ">", 376 "events <", 377 " if_name: \"\"", 378 " link_layer: 4", 379 " network_id: 100", 380 " time_ms: 0", 381 " transports: 2", 382 " dns_lookup_batch <", 383 " event_types: 1", 384 " event_types: 1", 385 " event_types: 2", 386 " getaddrinfo_error_count: 0", 387 " getaddrinfo_query_count: 0", 388 " gethostbyname_error_count: 0", 389 " gethostbyname_query_count: 0", 390 " latencies_ms: 3456", 391 " latencies_ms: 45", 392 " latencies_ms: 638", 393 " return_codes: 0", 394 " return_codes: 3", 395 " return_codes: 0", 396 " >", 397 ">", 398 "events <", 399 " if_name: \"\"", 400 " link_layer: 2", 401 " network_id: 101", 402 " time_ms: 0", 403 " transports: 1", 404 " dns_lookup_batch <", 405 " event_types: 1", 406 " event_types: 2", 407 " getaddrinfo_error_count: 0", 408 " getaddrinfo_query_count: 0", 409 " gethostbyname_error_count: 0", 410 " gethostbyname_query_count: 0", 411 " latencies_ms: 56", 412 " latencies_ms: 34", 413 " return_codes: 0", 414 " return_codes: 0", 415 " >", 416 ">", 417 "events <", 418 " if_name: \"\"", 419 " link_layer: 2", 420 " network_id: 0", 421 " time_ms: 0", 422 " transports: 0", 423 " wakeup_stats <", 424 " application_wakeups: 2", 425 " duration_sec: 0", 426 " no_uid_wakeups: 0", 427 " non_application_wakeups: 0", 428 " root_wakeups: 0", 429 " system_wakeups: 1", 430 " total_wakeups: 3", 431 " >", 432 ">", 433 "events <", 434 " if_name: \"\"", 435 " link_layer: 4", 436 " network_id: 0", 437 " time_ms: 0", 438 " transports: 0", 439 " wakeup_stats <", 440 " application_wakeups: 1", 441 " duration_sec: 0", 442 " no_uid_wakeups: 1", 443 " non_application_wakeups: 0", 444 " root_wakeups: 0", 445 " system_wakeups: 2", 446 " total_wakeups: 4", 447 " >", 448 ">", 449 "version: 2\n"); 450 451 verifySerialization(want, getdump("flush")); 452 } 453 454 String getdump(String ... command) { 455 StringWriter buffer = new StringWriter(); 456 PrintWriter writer = new PrintWriter(buffer); 457 mService.impl.dump(null, writer, command); 458 return buffer.toString(); 459 } 460 461 void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception { 462 mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1); 463 } 464 465 void dnsEvent(int netId, int type, int result, int latency) throws Exception { 466 mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); 467 } 468 469 void wakeupEvent(String iface, int uid) throws Exception { 470 String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface; 471 mNetdListener.onWakeupEvent(prefix, uid, uid, 0); 472 } 473 474 List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception { 475 ArgumentCaptor<ConnectivityMetricsEvent> captor = 476 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); 477 verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture()); 478 return captor.getAllValues(); 479 } 480 481 List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception { 482 return verifyEvents(n, 10); 483 } 484 485 static void verifySerialization(String want, String output) { 486 try { 487 byte[] got = Base64.decode(output, Base64.DEFAULT); 488 IpConnectivityLogClass.IpConnectivityLog log = 489 IpConnectivityLogClass.IpConnectivityLog.parseFrom(got); 490 assertEquals(want, log.toString()); 491 } catch (Exception e) { 492 fail(e.toString()); 493 } 494 } 495 496 static String joinLines(String ... elems) { 497 StringBuilder b = new StringBuilder(); 498 for (String s : elems) { 499 b.append(s).append("\n"); 500 } 501 return b.toString(); 502 } 503 504 static ConnectivityMetricsEvent expectedEvent(int timestamp) { 505 ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); 506 ev.timestamp = timestamp; 507 ev.data = FAKE_EV; 508 return ev; 509 } 510 511 /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ 512 static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) { 513 assertEquals(expected.timestamp, got.timestamp); 514 assertEquals(expected.data, got.data); 515 } 516 517 static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR = 518 Comparator.comparingLong((ev) -> ev.timestamp); 519 } 520