1 /*
2  * Copyright (C) 2019 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 android.net.metrics;
18 
19 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
21 
22 import static com.android.net.module.util.BitUtils.unpackBits;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 
29 import android.net.ConnectivityMetricsEvent;
30 import android.net.IIpConnectivityMetrics;
31 import android.net.Network;
32 
33 import androidx.test.filters.SmallTest;
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.mockito.ArgumentCaptor;
40 import org.mockito.Mock;
41 import org.mockito.MockitoAnnotations;
42 
43 import java.util.Collections;
44 import java.util.Comparator;
45 import java.util.Iterator;
46 import java.util.List;
47 
48 @RunWith(AndroidJUnit4.class)
49 @SmallTest
50 public class IpConnectivityLogTest {
51     private static final int FAKE_NET_ID = 100;
52     private static final int[] FAKE_TRANSPORT_TYPES = unpackBits(TRANSPORT_WIFI);
53     private static final long FAKE_TIME_STAMP = System.currentTimeMillis();
54     private static final long THREAD_TIMEOUT_MS = 10_000L;
55     private static final String FAKE_INTERFACE_NAME = "test";
56     private static final IpReachabilityEvent FAKE_EV =
57             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
58 
59     @Mock IIpConnectivityMetrics mMockService;
60 
61     @Before
setUp()62     public void setUp() {
63         MockitoAnnotations.initMocks(this);
64     }
65 
66     @Test
testLoggingEvents()67     public void testLoggingEvents() throws Exception {
68         IpConnectivityLog logger = new IpConnectivityLog(mMockService);
69 
70         assertTrue(logger.log(FAKE_EV));
71         assertTrue(logger.log(FAKE_TIME_STAMP, FAKE_EV));
72         assertTrue(logger.log(FAKE_NET_ID, FAKE_TRANSPORT_TYPES, FAKE_EV));
73         assertTrue(logger.log(new Network(FAKE_NET_ID), FAKE_TRANSPORT_TYPES, FAKE_EV));
74         assertTrue(logger.log(FAKE_INTERFACE_NAME, FAKE_EV));
75         assertTrue(logger.log(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID, TRANSPORT_WIFI,
76                 FAKE_INTERFACE_NAME)));
77 
78         List<ConnectivityMetricsEvent> got = verifyEvents(6);
79         assertEventsEqual(makeExpectedEvent(got.get(0).timestamp, 0, 0, null), got.get(0));
80         assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, 0, 0, null), got.get(1));
81         assertEventsEqual(makeExpectedEvent(got.get(2).timestamp, FAKE_NET_ID,
82                 TRANSPORT_WIFI, null), got.get(2));
83         assertEventsEqual(makeExpectedEvent(got.get(3).timestamp, FAKE_NET_ID,
84                 TRANSPORT_WIFI, null), got.get(3));
85         assertEventsEqual(makeExpectedEvent(got.get(4).timestamp, 0, 0, FAKE_INTERFACE_NAME),
86                 got.get(4));
87         assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID,
88                 TRANSPORT_WIFI, FAKE_INTERFACE_NAME), got.get(5));
89     }
90 
91     @Test
testLoggingEventsWithMultipleCallers()92     public void testLoggingEventsWithMultipleCallers() throws Exception {
93         IpConnectivityLog logger = new IpConnectivityLog(mMockService);
94 
95         final int nCallers = 10;
96         final int nEvents = 10;
97         final Thread[] threads = new Thread[nCallers];
98         for (int n = 0; n < nCallers; n++) {
99             final int i = n;
100             threads[i] = new Thread(() -> {
101                 for (int j = 0; j < nEvents; j++) {
102                     assertTrue(logger.log(makeExpectedEvent(
103                             FAKE_TIME_STAMP + i * 100 + j,
104                             FAKE_NET_ID + i * 100 + j,
105                             ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR,
106                             FAKE_INTERFACE_NAME)));
107                 }
108             });
109             threads[i].start();
110         }
111         // To ensure the events have been sent out on each thread. Wait for the thread to die.
112         for (Thread thread : threads) {
113             thread.join(THREAD_TIMEOUT_MS);
114         }
115 
116         final List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents);
117         Collections.sort(got, EVENT_COMPARATOR);
118         Iterator<ConnectivityMetricsEvent> iter = got.iterator();
119         for (int i = 0; i < nCallers; i++) {
120             for (int j = 0; j < nEvents; j++) {
121                 final long expectedTimestamp = FAKE_TIME_STAMP + i * 100 + j;
122                 final int expectedNetId = FAKE_NET_ID + i * 100 + j;
123                 final long expectedTransports =
124                         ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR;
125                 assertEventsEqual(makeExpectedEvent(expectedTimestamp, expectedNetId,
126                         expectedTransports, FAKE_INTERFACE_NAME), iter.next());
127             }
128         }
129     }
130 
verifyEvents(int n)131     private List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
132         ArgumentCaptor<ConnectivityMetricsEvent> captor =
133                 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
134         verify(mMockService, times(n)).logEvent(captor.capture());
135         return captor.getAllValues();
136     }
137 
138 
makeExpectedEvent(long timestamp, int netId, long transports, String ifname)139     private ConnectivityMetricsEvent makeExpectedEvent(long timestamp, int netId, long transports,
140             String ifname) {
141         ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
142         ev.timestamp = timestamp;
143         ev.data = FAKE_EV;
144         ev.netId = netId;
145         ev.transports = transports;
146         ev.ifname = ifname;
147         return ev;
148     }
149 
150     /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got)151     private void assertEventsEqual(ConnectivityMetricsEvent expected,
152             ConnectivityMetricsEvent got) {
153         assertEquals(expected.data, got.data);
154         assertEquals(expected.timestamp, got.timestamp);
155         assertEquals(expected.netId, got.netId);
156         assertEquals(expected.transports, got.transports);
157         assertEquals(expected.ifname, got.ifname);
158     }
159 
160     static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
161             Comparator.comparingLong((ev) -> ev.timestamp);
162 }
163