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.mock;
22 import static org.mockito.Mockito.timeout;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 
30 import android.content.Context;
31 import android.net.ConnectivityManager;
32 import android.net.ConnectivityMetricsEvent;
33 import android.net.IIpConnectivityMetrics;
34 import android.net.IpPrefix;
35 import android.net.LinkAddress;
36 import android.net.LinkProperties;
37 import android.net.RouteInfo;
38 import android.net.Network;
39 import android.net.NetworkCapabilities;
40 import android.net.metrics.ApfProgramEvent;
41 import android.net.metrics.ApfStats;
42 import android.net.metrics.DefaultNetworkEvent;
43 import android.net.metrics.DhcpClientEvent;
44 import android.net.metrics.IpConnectivityLog;
45 import android.net.metrics.IpManagerEvent;
46 import android.net.metrics.IpReachabilityEvent;
47 import android.net.metrics.RaEvent;
48 import android.net.metrics.ValidationProbeEvent;
49 import android.os.Parcelable;
50 import android.support.test.runner.AndroidJUnit4;
51 import android.system.OsConstants;
52 import android.test.suitebuilder.annotation.SmallTest;
53 import android.util.Base64;
54 
55 import com.android.internal.util.BitUtils;
56 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
57 
58 import java.io.PrintWriter;
59 import java.io.StringWriter;
60 import java.util.Collections;
61 import java.util.Comparator;
62 import java.util.Iterator;
63 import java.util.List;
64 
65 import org.mockito.ArgumentCaptor;
66 import org.mockito.Mock;
67 import org.mockito.MockitoAnnotations;
68 import org.junit.Before;
69 import org.junit.Test;
70 import org.junit.runner.RunWith;
71 
72 @RunWith(AndroidJUnit4.class)
73 @SmallTest
74 public class IpConnectivityMetricsTest {
75     static final IpReachabilityEvent FAKE_EV =
76             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
77 
78     private static final String EXAMPLE_IPV4 = "192.0.2.1";
79     private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
80 
81     private static final byte[] MAC_ADDR =
82             {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
83 
84     @Mock Context mCtx;
85     @Mock IIpConnectivityMetrics mMockService;
86     @Mock ConnectivityManager mCm;
87 
88     IpConnectivityMetrics mService;
89     NetdEventListenerService mNetdListener;
90 
91     @Before
setUp()92     public void setUp() {
93         MockitoAnnotations.initMocks(this);
94         mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
95         mNetdListener = new NetdEventListenerService(mCm);
96         mService.mNetdListener = mNetdListener;
97     }
98 
99     @Test
testLoggingEvents()100     public void testLoggingEvents() throws Exception {
101         IpConnectivityLog logger = new IpConnectivityLog(mMockService);
102 
103         assertTrue(logger.log(1, FAKE_EV));
104         assertTrue(logger.log(2, FAKE_EV));
105         assertTrue(logger.log(3, FAKE_EV));
106 
107         List<ConnectivityMetricsEvent> got = verifyEvents(3);
108         assertEventsEqual(expectedEvent(1), got.get(0));
109         assertEventsEqual(expectedEvent(2), got.get(1));
110         assertEventsEqual(expectedEvent(3), got.get(2));
111     }
112 
113     @Test
testLoggingEventsWithMultipleCallers()114     public void testLoggingEventsWithMultipleCallers() throws Exception {
115         IpConnectivityLog logger = new IpConnectivityLog(mMockService);
116 
117         final int nCallers = 10;
118         final int nEvents = 10;
119         for (int n = 0; n < nCallers; n++) {
120             final int i = n;
121             new Thread() {
122                 public void run() {
123                     for (int j = 0; j < nEvents; j++) {
124                         assertTrue(logger.log(1 + i * 100 + j, FAKE_EV));
125                     }
126                 }
127             }.start();
128         }
129 
130         List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200);
131         Collections.sort(got, EVENT_COMPARATOR);
132         Iterator<ConnectivityMetricsEvent> iter = got.iterator();
133         for (int i = 0; i < nCallers; i++) {
134             for (int j = 0; j < nEvents; j++) {
135                 int expectedTimestamp = 1 + i * 100 + j;
136                 assertEventsEqual(expectedEvent(expectedTimestamp), iter.next());
137             }
138         }
139     }
140 
141     @Test
testBufferFlushing()142     public void testBufferFlushing() {
143         String output1 = getdump("flush");
144         assertEquals("", output1);
145 
146         new IpConnectivityLog(mService.impl).log(1, FAKE_EV);
147         String output2 = getdump("flush");
148         assertFalse("".equals(output2));
149 
150         String output3 = getdump("flush");
151         assertEquals("", output3);
152     }
153 
154     @Test
testRateLimiting()155     public void testRateLimiting() {
156         final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
157         final ApfProgramEvent ev = new ApfProgramEvent();
158         final long fakeTimestamp = 1;
159 
160         int attempt = 100; // More than burst quota, but less than buffer size.
161         for (int i = 0; i < attempt; i++) {
162             logger.log(ev);
163         }
164 
165         String output1 = getdump("flush");
166         assertFalse("".equals(output1));
167 
168         for (int i = 0; i < attempt; i++) {
169             assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev));
170         }
171 
172         String output2 = getdump("flush");
173         assertEquals("", output2);
174     }
175 
176     @Test
testDefaultNetworkEvents()177     public void testDefaultNetworkEvents() throws Exception {
178         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
179         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
180 
181         NetworkAgentInfo[][] defaultNetworks = {
182             // nothing -> cell
183             {null, makeNai(100, 10, false, true, cell)},
184             // cell -> wifi
185             {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)},
186             // wifi -> nothing
187             {makeNai(101, 60, true, false, wifi), null},
188             // nothing -> cell
189             {null, makeNai(102, 10, true, true, cell)},
190             // cell -> wifi
191             {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
192         };
193 
194         long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
195         long durationMs = 1001;
196         for (NetworkAgentInfo[] pair : defaultNetworks) {
197             timeMs += durationMs;
198             durationMs += durationMs;
199             mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
200         }
201 
202         String want = String.join("\n",
203                 "dropped_events: 0",
204                 "events <",
205                 "  if_name: \"\"",
206                 "  link_layer: 5",
207                 "  network_id: 0",
208                 "  time_ms: 0",
209                 "  transports: 0",
210                 "  default_network_event <",
211                 "    default_network_duration_ms: 1001",
212                 "    final_score: 0",
213                 "    initial_score: 0",
214                 "    ip_support: 0",
215                 "    no_default_network_duration_ms: 0",
216                 "    previous_default_network_link_layer: 0",
217                 "    previous_network_ip_support: 0",
218                 "    validation_duration_ms: 0",
219                 "  >",
220                 ">",
221                 "events <",
222                 "  if_name: \"\"",
223                 "  link_layer: 2",
224                 "  network_id: 100",
225                 "  time_ms: 0",
226                 "  transports: 1",
227                 "  default_network_event <",
228                 "    default_network_duration_ms: 2002",
229                 "    final_score: 50",
230                 "    initial_score: 10",
231                 "    ip_support: 3",
232                 "    no_default_network_duration_ms: 0",
233                 "    previous_default_network_link_layer: 0",
234                 "    previous_network_ip_support: 0",
235                 "    validation_duration_ms: 2002",
236                 "  >",
237                 ">",
238                 "events <",
239                 "  if_name: \"\"",
240                 "  link_layer: 4",
241                 "  network_id: 101",
242                 "  time_ms: 0",
243                 "  transports: 2",
244                 "  default_network_event <",
245                 "    default_network_duration_ms: 4004",
246                 "    final_score: 60",
247                 "    initial_score: 20",
248                 "    ip_support: 1",
249                 "    no_default_network_duration_ms: 0",
250                 "    previous_default_network_link_layer: 2",
251                 "    previous_network_ip_support: 0",
252                 "    validation_duration_ms: 4004",
253                 "  >",
254                 ">",
255                 "events <",
256                 "  if_name: \"\"",
257                 "  link_layer: 5",
258                 "  network_id: 0",
259                 "  time_ms: 0",
260                 "  transports: 0",
261                 "  default_network_event <",
262                 "    default_network_duration_ms: 8008",
263                 "    final_score: 0",
264                 "    initial_score: 0",
265                 "    ip_support: 0",
266                 "    no_default_network_duration_ms: 0",
267                 "    previous_default_network_link_layer: 4",
268                 "    previous_network_ip_support: 0",
269                 "    validation_duration_ms: 0",
270                 "  >",
271                 ">",
272                 "events <",
273                 "  if_name: \"\"",
274                 "  link_layer: 2",
275                 "  network_id: 102",
276                 "  time_ms: 0",
277                 "  transports: 1",
278                 "  default_network_event <",
279                 "    default_network_duration_ms: 16016",
280                 "    final_score: 50",
281                 "    initial_score: 10",
282                 "    ip_support: 3",
283                 "    no_default_network_duration_ms: 0",
284                 "    previous_default_network_link_layer: 4",
285                 "    previous_network_ip_support: 0",
286                 "    validation_duration_ms: 16016",
287                 "  >",
288                 ">",
289                 "version: 2\n");
290 
291         verifySerialization(want, getdump("flush"));
292     }
293 
294     @Test
testEndToEndLogging()295     public void testEndToEndLogging() throws Exception {
296         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
297         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
298 
299         NetworkCapabilities ncWifi = new NetworkCapabilities();
300         NetworkCapabilities ncCell = new NetworkCapabilities();
301         ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
302         ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
303 
304         when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
305         when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
306 
307         ApfStats apfStats = new ApfStats();
308         apfStats.durationMs = 45000;
309         apfStats.receivedRas = 10;
310         apfStats.matchingRas = 2;
311         apfStats.droppedRas = 2;
312         apfStats.parseErrors = 2;
313         apfStats.zeroLifetimeRas = 1;
314         apfStats.programUpdates = 4;
315         apfStats.programUpdatesAll = 7;
316         apfStats.programUpdatesAllowingMulticast = 3;
317         apfStats.maxProgramSize = 2048;
318 
319         ValidationProbeEvent validationEv = new ValidationProbeEvent();
320         validationEv.durationMs = 40730;
321         validationEv.probeType = ValidationProbeEvent.PROBE_HTTP;
322         validationEv.returnCode = 204;
323 
324         Parcelable[] events = {
325             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED),
326             new DhcpClientEvent("SomeState", 192),
327             new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678),
328             validationEv,
329             apfStats,
330             new RaEvent(2000, 400, 300, -1, 1000, -1)
331         };
332 
333         for (int i = 0; i < events.length; i++) {
334             ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
335             ev.timestamp = 100 * (i + 1);
336             ev.ifname = "wlan0";
337             ev.data = events[i];
338             logger.log(ev);
339         }
340 
341         // netId, errno, latency, destination
342         connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4);
343         connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6);
344         connectEvent(100, 0, 110, EXAMPLE_IPV4);
345         connectEvent(101, 0, 23, EXAMPLE_IPV4);
346         connectEvent(101, 0, 45, EXAMPLE_IPV6);
347         connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4);
348 
349         // netId, type, return code, latency
350         dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
351         dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
352         dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638);
353         dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
354         dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
355 
356         // iface, uid
357         final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b};
358         final String srcIp = "192.168.2.1";
359         final String dstIp = "192.168.2.23";
360         final int sport = 2356;
361         final int dport = 13489;
362         final long now = 1001L;
363         final int v4 = 0x800;
364         final int tcp = 6;
365         final int udp = 17;
366         wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
367         wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
368         wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
369         wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
370         wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
371         wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
372 
373         long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
374         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
375         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
376         NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
377         NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
378         mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null);
379         mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai);
380 
381         String want = String.join("\n",
382                 "dropped_events: 0",
383                 "events <",
384                 "  if_name: \"\"",
385                 "  link_layer: 4",
386                 "  network_id: 0",
387                 "  time_ms: 100",
388                 "  transports: 0",
389                 "  ip_reachability_event <",
390                 "    event_type: 512",
391                 "    if_name: \"\"",
392                 "  >",
393                 ">",
394                 "events <",
395                 "  if_name: \"\"",
396                 "  link_layer: 4",
397                 "  network_id: 0",
398                 "  time_ms: 200",
399                 "  transports: 0",
400                 "  dhcp_event <",
401                 "    duration_ms: 192",
402                 "    if_name: \"\"",
403                 "    state_transition: \"SomeState\"",
404                 "  >",
405                 ">",
406                 "events <",
407                 "  if_name: \"\"",
408                 "  link_layer: 4",
409                 "  network_id: 0",
410                 "  time_ms: 300",
411                 "  transports: 0",
412                 "  ip_provisioning_event <",
413                 "    event_type: 1",
414                 "    if_name: \"\"",
415                 "    latency_ms: 5678",
416                 "  >",
417                 ">",
418                 "events <",
419                 "  if_name: \"\"",
420                 "  link_layer: 4",
421                 "  network_id: 0",
422                 "  time_ms: 400",
423                 "  transports: 0",
424                 "  validation_probe_event <",
425                 "    latency_ms: 40730",
426                 "    probe_result: 204",
427                 "    probe_type: 1",
428                 "  >",
429                 ">",
430                 "events <",
431                 "  if_name: \"\"",
432                 "  link_layer: 4",
433                 "  network_id: 0",
434                 "  time_ms: 500",
435                 "  transports: 0",
436                 "  apf_statistics <",
437                 "    dropped_ras: 2",
438                 "    duration_ms: 45000",
439                 "    matching_ras: 2",
440                 "    max_program_size: 2048",
441                 "    parse_errors: 2",
442                 "    program_updates: 4",
443                 "    program_updates_all: 7",
444                 "    program_updates_allowing_multicast: 3",
445                 "    received_ras: 10",
446                 "    total_packet_dropped: 0",
447                 "    total_packet_processed: 0",
448                 "    zero_lifetime_ras: 1",
449                 "  >",
450                 ">",
451                 "events <",
452                 "  if_name: \"\"",
453                 "  link_layer: 4",
454                 "  network_id: 0",
455                 "  time_ms: 600",
456                 "  transports: 0",
457                 "  ra_event <",
458                 "    dnssl_lifetime: -1",
459                 "    prefix_preferred_lifetime: 300",
460                 "    prefix_valid_lifetime: 400",
461                 "    rdnss_lifetime: 1000",
462                 "    route_info_lifetime: -1",
463                 "    router_lifetime: 2000",
464                 "  >",
465                 ">",
466                 "events <",
467                 "  if_name: \"\"",
468                 "  link_layer: 5",
469                 "  network_id: 0",
470                 "  time_ms: 0",
471                 "  transports: 0",
472                 "  default_network_event <",
473                 "    default_network_duration_ms: 200",
474                 "    final_score: 0",
475                 "    initial_score: 0",
476                 "    ip_support: 0",
477                 "    no_default_network_duration_ms: 0",
478                 "    previous_default_network_link_layer: 0",
479                 "    previous_network_ip_support: 0",
480                 "    validation_duration_ms: 0",
481                 "  >",
482                 ">",
483                 "events <",
484                 "  if_name: \"\"",
485                 "  link_layer: 2",
486                 "  network_id: 100",
487                 "  time_ms: 0",
488                 "  transports: 1",
489                 "  default_network_event <",
490                 "    default_network_duration_ms: 100",
491                 "    final_score: 50",
492                 "    initial_score: 50",
493                 "    ip_support: 2",
494                 "    no_default_network_duration_ms: 0",
495                 "    previous_default_network_link_layer: 0",
496                 "    previous_network_ip_support: 0",
497                 "    validation_duration_ms: 100",
498                 "  >",
499                 ">",
500                 "events <",
501                 "  if_name: \"\"",
502                 "  link_layer: 4",
503                 "  network_id: 100",
504                 "  time_ms: 0",
505                 "  transports: 2",
506                 "  connect_statistics <",
507                 "    connect_blocking_count: 1",
508                 "    connect_count: 3",
509                 "    errnos_counters <",
510                 "      key: 11",
511                 "      value: 1",
512                 "    >",
513                 "    ipv6_addr_count: 1",
514                 "    latencies_ms: 110",
515                 "  >",
516                 ">",
517                 "events <",
518                 "  if_name: \"\"",
519                 "  link_layer: 2",
520                 "  network_id: 101",
521                 "  time_ms: 0",
522                 "  transports: 1",
523                 "  connect_statistics <",
524                 "    connect_blocking_count: 2",
525                 "    connect_count: 2",
526                 "    ipv6_addr_count: 1",
527                 "    latencies_ms: 23",
528                 "    latencies_ms: 45",
529                 "  >",
530                 ">",
531                 "events <",
532                 "  if_name: \"\"",
533                 "  link_layer: 4",
534                 "  network_id: 100",
535                 "  time_ms: 0",
536                 "  transports: 2",
537                 "  dns_lookup_batch <",
538                 "    event_types: 1",
539                 "    event_types: 1",
540                 "    event_types: 2",
541                 "    getaddrinfo_error_count: 0",
542                 "    getaddrinfo_query_count: 0",
543                 "    gethostbyname_error_count: 0",
544                 "    gethostbyname_query_count: 0",
545                 "    latencies_ms: 3456",
546                 "    latencies_ms: 45",
547                 "    latencies_ms: 638",
548                 "    return_codes: 0",
549                 "    return_codes: 3",
550                 "    return_codes: 0",
551                 "  >",
552                 ">",
553                 "events <",
554                 "  if_name: \"\"",
555                 "  link_layer: 2",
556                 "  network_id: 101",
557                 "  time_ms: 0",
558                 "  transports: 1",
559                 "  dns_lookup_batch <",
560                 "    event_types: 1",
561                 "    event_types: 2",
562                 "    getaddrinfo_error_count: 0",
563                 "    getaddrinfo_query_count: 0",
564                 "    gethostbyname_error_count: 0",
565                 "    gethostbyname_query_count: 0",
566                 "    latencies_ms: 56",
567                 "    latencies_ms: 34",
568                 "    return_codes: 0",
569                 "    return_codes: 0",
570                 "  >",
571                 ">",
572                 "events <",
573                 "  if_name: \"\"",
574                 "  link_layer: 4",
575                 "  network_id: 0",
576                 "  time_ms: 0",
577                 "  transports: 0",
578                 "  wakeup_stats <",
579                 "    application_wakeups: 3",
580                 "    duration_sec: 0",
581                 "    ethertype_counts <",
582                 "      key: 2048",
583                 "      value: 6",
584                 "    >",
585                 "    ip_next_header_counts <",
586                 "      key: 6",
587                 "      value: 3",
588                 "    >",
589                 "    ip_next_header_counts <",
590                 "      key: 17",
591                 "      value: 3",
592                 "    >",
593                 "    l2_broadcast_count: 0",
594                 "    l2_multicast_count: 0",
595                 "    l2_unicast_count: 6",
596                 "    no_uid_wakeups: 1",
597                 "    non_application_wakeups: 0",
598                 "    root_wakeups: 0",
599                 "    system_wakeups: 2",
600                 "    total_wakeups: 6",
601                 "  >",
602                 ">",
603                 "version: 2\n");
604 
605         verifySerialization(want, getdump("flush"));
606     }
607 
getdump(String .... command)608     String getdump(String ... command) {
609         StringWriter buffer = new StringWriter();
610         PrintWriter writer = new PrintWriter(buffer);
611         mService.impl.dump(null, writer, command);
612         return buffer.toString();
613     }
614 
connectEvent(int netid, int error, int latencyMs, String ipAddr)615     void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception {
616         mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1);
617     }
618 
dnsEvent(int netId, int type, int result, int latency)619     void dnsEvent(int netId, int type, int result, int latency) throws Exception {
620         mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
621     }
622 
wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, String dstIp, int sport, int dport, long now)623     void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
624             String dstIp, int sport, int dport, long now) throws Exception {
625         String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
626         mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
627     }
628 
makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports)629     NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
630         NetworkAgentInfo nai = mock(NetworkAgentInfo.class);
631         when(nai.network()).thenReturn(new Network(netId));
632         when(nai.getCurrentScore()).thenReturn(score);
633         nai.linkProperties = new LinkProperties();
634         nai.networkCapabilities = new NetworkCapabilities();
635         nai.lastValidated = true;
636         for (int t : BitUtils.unpackBits(transports)) {
637             nai.networkCapabilities.addTransportType(t);
638         }
639         if (ipv4) {
640             nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24"));
641             nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0")));
642         }
643         if (ipv6) {
644             nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64"));
645             nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0")));
646         }
647         return nai;
648     }
649 
verifyEvents(int n, int timeoutMs)650     List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
651         ArgumentCaptor<ConnectivityMetricsEvent> captor =
652                 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
653         verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture());
654         return captor.getAllValues();
655     }
656 
verifyEvents(int n)657     List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
658         return verifyEvents(n, 10);
659     }
660 
verifySerialization(String want, String output)661     static void verifySerialization(String want, String output) {
662         try {
663             byte[] got = Base64.decode(output, Base64.DEFAULT);
664             IpConnectivityLogClass.IpConnectivityLog log =
665                     IpConnectivityLogClass.IpConnectivityLog.parseFrom(got);
666             assertEquals(want, log.toString());
667         } catch (Exception e) {
668             fail(e.toString());
669         }
670     }
671 
joinLines(String .... elems)672     static String joinLines(String ... elems) {
673         StringBuilder b = new StringBuilder();
674         for (String s : elems) {
675             b.append(s).append("\n");
676         }
677         return b.toString();
678     }
679 
expectedEvent(int timestamp)680     static ConnectivityMetricsEvent expectedEvent(int timestamp) {
681         ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
682         ev.timestamp = timestamp;
683         ev.data = FAKE_EV;
684         return ev;
685     }
686 
687     /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got)688     static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
689         assertEquals(expected.timestamp, got.timestamp);
690         assertEquals(expected.data, got.data);
691     }
692 
693     static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
694         Comparator.comparingLong((ev) -> ev.timestamp);
695 }
696