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.wifi;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Mockito.anyInt;
24 import static org.mockito.Mockito.anyString;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.test.suitebuilder.annotation.SmallTest;
30 
31 import org.junit.Before;
32 import org.junit.Test;
33 
34 import java.lang.reflect.Constructor;
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.regex.Pattern;
38 
39 /**
40  * Unit tests for {@link com.android.server.wifi.WifiNative}.
41  */
42 @SmallTest
43 public class WifiNativeTest {
44     private static final int NETWORK_ID = 0;
45     private static final String NETWORK_EXTRAS_VARIABLE = "test";
46     private static final Map<String, String> NETWORK_EXTRAS_VALUES = new HashMap<>();
47     static {
48         NETWORK_EXTRAS_VALUES.put("key1", "value1");
49         NETWORK_EXTRAS_VALUES.put("key2", "value2");
50     }
51     private static final String NETWORK_EXTRAS_SERIALIZED =
52             "\"%7B%22key2%22%3A%22value2%22%2C%22key1%22%3A%22value1%22%7D\"";
53 
54     private static final long FATE_REPORT_DRIVER_TIMESTAMP_USEC = 12345;
55     private static final byte[] FATE_REPORT_FRAME_BYTES = new byte[] {
56             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 0, 1, 2, 3, 4, 5, 6, 7};
57     private static final WifiNative.TxFateReport TX_FATE_REPORT = new WifiNative.TxFateReport(
58             WifiLoggerHal.TX_PKT_FATE_SENT,
59             FATE_REPORT_DRIVER_TIMESTAMP_USEC,
60             WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
61             FATE_REPORT_FRAME_BYTES
62     );
63     private static final WifiNative.RxFateReport RX_FATE_REPORT = new WifiNative.RxFateReport(
64             WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID,
65             FATE_REPORT_DRIVER_TIMESTAMP_USEC,
66             WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
67             FATE_REPORT_FRAME_BYTES
68     );
69     private static final FrameTypeMapping[] FRAME_TYPE_MAPPINGS = new FrameTypeMapping[] {
70             new FrameTypeMapping(WifiLoggerHal.FRAME_TYPE_UNKNOWN, "unknown", "N/A"),
71             new FrameTypeMapping(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, "data", "Ethernet"),
72             new FrameTypeMapping(WifiLoggerHal.FRAME_TYPE_80211_MGMT, "802.11 management",
73                     "802.11 Mgmt"),
74             new FrameTypeMapping((byte) 42, "42", "N/A")
75     };
76     private static final FateMapping[] TX_FATE_MAPPINGS = new FateMapping[] {
77             new FateMapping(WifiLoggerHal.TX_PKT_FATE_ACKED, "acked"),
78             new FateMapping(WifiLoggerHal.TX_PKT_FATE_SENT, "sent"),
79             new FateMapping(WifiLoggerHal.TX_PKT_FATE_FW_QUEUED, "firmware queued"),
80             new FateMapping(WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID,
81                     "firmware dropped (invalid frame)"),
82             new FateMapping(
83                     WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS,  "firmware dropped (no bufs)"),
84             new FateMapping(
85                     WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER, "firmware dropped (other)"),
86             new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED, "driver queued"),
87             new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID,
88                     "driver dropped (invalid frame)"),
89             new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS,
90                     "driver dropped (no bufs)"),
91             new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER, "driver dropped (other)"),
92             new FateMapping((byte) 42, "42")
93     };
94     private static final FateMapping[] RX_FATE_MAPPINGS = new FateMapping[] {
95             new FateMapping(WifiLoggerHal.RX_PKT_FATE_SUCCESS, "success"),
96             new FateMapping(WifiLoggerHal.RX_PKT_FATE_FW_QUEUED, "firmware queued"),
97             new FateMapping(
98                     WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER, "firmware dropped (filter)"),
99             new FateMapping(WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID,
100                     "firmware dropped (invalid frame)"),
101             new FateMapping(
102                     WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS, "firmware dropped (no bufs)"),
103             new FateMapping(
104                     WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER, "firmware dropped (other)"),
105             new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED, "driver queued"),
106             new FateMapping(
107                     WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER, "driver dropped (filter)"),
108             new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID,
109                     "driver dropped (invalid frame)"),
110             new FateMapping(
111                     WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS, "driver dropped (no bufs)"),
112             new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER, "driver dropped (other)"),
113             new FateMapping((byte) 42, "42")
114     };
115 
116     private WifiNative mWifiNative;
117 
118     @Before
setUp()119     public void setUp() throws Exception {
120         final Constructor<WifiNative> wifiNativeConstructor =
121                 WifiNative.class.getDeclaredConstructor(String.class, Boolean.TYPE);
122         wifiNativeConstructor.setAccessible(true);
123         mWifiNative = spy(wifiNativeConstructor.newInstance("test", true));
124     }
125 
126     /**
127      * Verifies that setNetworkExtra() correctly writes a serialized and URL-encoded JSON object.
128      */
129     @Test
testSetNetworkExtra()130     public void testSetNetworkExtra() {
131         when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true);
132         assertTrue(mWifiNative.setNetworkExtra(NETWORK_ID, NETWORK_EXTRAS_VARIABLE,
133                 NETWORK_EXTRAS_VALUES));
134         verify(mWifiNative).setNetworkVariable(NETWORK_ID, NETWORK_EXTRAS_VARIABLE,
135                 NETWORK_EXTRAS_SERIALIZED);
136     }
137 
138     /**
139      * Verifies that getNetworkExtra() correctly reads a serialized and URL-encoded JSON object.
140      */
141     @Test
testGetNetworkExtra()142     public void testGetNetworkExtra() {
143         when(mWifiNative.getNetworkVariable(NETWORK_ID, NETWORK_EXTRAS_VARIABLE))
144                 .thenReturn(NETWORK_EXTRAS_SERIALIZED);
145         final Map<String, String> actualValues =
146                 mWifiNative.getNetworkExtra(NETWORK_ID, NETWORK_EXTRAS_VARIABLE);
147         assertEquals(NETWORK_EXTRAS_VALUES, actualValues);
148     }
149 
150     /**
151      * Verifies that TxFateReport's constructor sets all of the TxFateReport fields.
152      */
153     @Test
testTxFateReportCtorSetsFields()154     public void testTxFateReportCtorSetsFields() {
155         WifiNative.TxFateReport fateReport = new WifiNative.TxFateReport(
156                 WifiLoggerHal.TX_PKT_FATE_SENT,  // non-zero value
157                 FATE_REPORT_DRIVER_TIMESTAMP_USEC,
158                 WifiLoggerHal.FRAME_TYPE_ETHERNET_II,  // non-zero value
159                 FATE_REPORT_FRAME_BYTES
160         );
161         assertEquals(WifiLoggerHal.TX_PKT_FATE_SENT, fateReport.mFate);
162         assertEquals(FATE_REPORT_DRIVER_TIMESTAMP_USEC, fateReport.mDriverTimestampUSec);
163         assertEquals(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, fateReport.mFrameType);
164         assertArrayEquals(FATE_REPORT_FRAME_BYTES, fateReport.mFrameBytes);
165     }
166 
167     /**
168      * Verifies that RxFateReport's constructor sets all of the RxFateReport fields.
169      */
170     @Test
testRxFateReportCtorSetsFields()171     public void testRxFateReportCtorSetsFields() {
172         WifiNative.RxFateReport fateReport = new WifiNative.RxFateReport(
173                 WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID,  // non-zero value
174                 FATE_REPORT_DRIVER_TIMESTAMP_USEC,
175                 WifiLoggerHal.FRAME_TYPE_ETHERNET_II,  // non-zero value
176                 FATE_REPORT_FRAME_BYTES
177         );
178         assertEquals(WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID, fateReport.mFate);
179         assertEquals(FATE_REPORT_DRIVER_TIMESTAMP_USEC, fateReport.mDriverTimestampUSec);
180         assertEquals(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, fateReport.mFrameType);
181         assertArrayEquals(FATE_REPORT_FRAME_BYTES, fateReport.mFrameBytes);
182     }
183 
184     // Support classes for test{Tx,Rx}FateReportToString.
185     private static class FrameTypeMapping {
186         byte mTypeNumber;
187         String mExpectedTypeText;
188         String mExpectedProtocolText;
FrameTypeMapping(byte typeNumber, String expectedTypeText, String expectedProtocolText)189         FrameTypeMapping(byte typeNumber, String expectedTypeText, String expectedProtocolText) {
190             this.mTypeNumber = typeNumber;
191             this.mExpectedTypeText = expectedTypeText;
192             this.mExpectedProtocolText = expectedProtocolText;
193         }
194     }
195     private static class FateMapping {
196         byte mFateNumber;
197         String mExpectedText;
FateMapping(byte fateNumber, String expectedText)198         FateMapping(byte fateNumber, String expectedText) {
199             this.mFateNumber = fateNumber;
200             this.mExpectedText = expectedText;
201         }
202     }
203 
204     /**
205      * Verifies that FateReport.getTableHeader() prints the right header.
206      */
207     @Test
testFateReportTableHeader()208     public void testFateReportTableHeader() {
209         final String header = WifiNative.FateReport.getTableHeader();
210         assertEquals(
211                 "\nTime usec        Walltime      Direction  Fate                              "
212                 + "Protocol      Type                     Result\n"
213                 + "---------        --------      ---------  ----                              "
214                 + "--------      ----                     ------\n", header);
215     }
216 
217     /**
218      * Verifies that TxFateReport.toTableRowString() includes the information we care about.
219      */
220     @Test
testTxFateReportToTableRowString()221     public void testTxFateReportToTableRowString() {
222         WifiNative.TxFateReport fateReport = TX_FATE_REPORT;
223         assertTrue(
224                 fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches(
225                     FATE_REPORT_DRIVER_TIMESTAMP_USEC + " "  // timestamp
226                             + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} "  // walltime
227                             + "TX "  // direction
228                             + "sent "  // fate
229                             + "Ethernet "  // type
230                             + "N/A "  // protocol
231                             + "N/A"  // result
232                 )
233         );
234 
235         for (FrameTypeMapping frameTypeMapping : FRAME_TYPE_MAPPINGS) {
236             fateReport = new WifiNative.TxFateReport(
237                     WifiLoggerHal.TX_PKT_FATE_SENT,
238                     FATE_REPORT_DRIVER_TIMESTAMP_USEC,
239                     frameTypeMapping.mTypeNumber,
240                     FATE_REPORT_FRAME_BYTES
241             );
242             assertTrue(
243                     fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches(
244                             FATE_REPORT_DRIVER_TIMESTAMP_USEC + " "  // timestamp
245                                     + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} "  // walltime
246                                     + "TX "  // direction
247                                     + "sent "  // fate
248                                     + frameTypeMapping.mExpectedProtocolText + " "  // type
249                                     + "N/A "  // protocol
250                                     + "N/A"  // result
251                     )
252             );
253         }
254 
255         for (FateMapping fateMapping : TX_FATE_MAPPINGS) {
256             fateReport = new WifiNative.TxFateReport(
257                     fateMapping.mFateNumber,
258                     FATE_REPORT_DRIVER_TIMESTAMP_USEC,
259                     WifiLoggerHal.FRAME_TYPE_80211_MGMT,
260                     FATE_REPORT_FRAME_BYTES
261             );
262             assertTrue(
263                     fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches(
264                             FATE_REPORT_DRIVER_TIMESTAMP_USEC + " "  // timestamp
265                                     + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} "  // walltime
266                                     + "TX "  // direction
267                                     + Pattern.quote(fateMapping.mExpectedText) + " "  // fate
268                                     + "802.11 Mgmt "  // type
269                                     + "N/A "  // protocol
270                                     + "N/A"  // result
271                     )
272             );
273         }
274     }
275 
276     /**
277      * Verifies that TxFateReport.toVerboseStringWithPiiAllowed() includes the information we care
278      * about.
279      */
280     @Test
testTxFateReportToVerboseStringWithPiiAllowed()281     public void testTxFateReportToVerboseStringWithPiiAllowed() {
282         WifiNative.TxFateReport fateReport = TX_FATE_REPORT;
283 
284         String verboseFateString = fateReport.toVerboseStringWithPiiAllowed();
285         assertTrue(verboseFateString.contains("Frame direction: TX"));
286         assertTrue(verboseFateString.contains("Frame timestamp: 12345"));
287         assertTrue(verboseFateString.contains("Frame fate: sent"));
288         assertTrue(verboseFateString.contains("Frame type: data"));
289         assertTrue(verboseFateString.contains("Frame protocol: Ethernet"));
290         assertTrue(verboseFateString.contains("Frame protocol type: N/A"));
291         assertTrue(verboseFateString.contains("Frame length: 16"));
292         assertTrue(verboseFateString.contains(
293                 "61 62 63 64 65 66 67 68 00 01 02 03 04 05 06 07")); // hex dump
294         // TODO(quiche): uncomment this, once b/27975149 is fixed.
295         // assertTrue(verboseFateString.contains("abcdefgh........"));  // hex dump
296 
297         for (FrameTypeMapping frameTypeMapping : FRAME_TYPE_MAPPINGS) {
298             fateReport = new WifiNative.TxFateReport(
299                     WifiLoggerHal.TX_PKT_FATE_SENT,
300                     FATE_REPORT_DRIVER_TIMESTAMP_USEC,
301                     frameTypeMapping.mTypeNumber,
302                     FATE_REPORT_FRAME_BYTES
303             );
304             verboseFateString = fateReport.toVerboseStringWithPiiAllowed();
305             assertTrue(verboseFateString.contains("Frame type: "
306                     + frameTypeMapping.mExpectedTypeText));
307         }
308 
309         for (FateMapping fateMapping : TX_FATE_MAPPINGS) {
310             fateReport = new WifiNative.TxFateReport(
311                     fateMapping.mFateNumber,
312                     FATE_REPORT_DRIVER_TIMESTAMP_USEC,
313                     WifiLoggerHal.FRAME_TYPE_80211_MGMT,
314                     FATE_REPORT_FRAME_BYTES
315             );
316             verboseFateString = fateReport.toVerboseStringWithPiiAllowed();
317             assertTrue(verboseFateString.contains("Frame fate: " + fateMapping.mExpectedText));
318         }
319     }
320 
321     /**
322      * Verifies that RxFateReport.toTableRowString() includes the information we care about.
323      */
324     @Test
testRxFateReportToTableRowString()325     public void testRxFateReportToTableRowString() {
326         WifiNative.RxFateReport fateReport = RX_FATE_REPORT;
327         assertTrue(
328                 fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches(
329                         FATE_REPORT_DRIVER_TIMESTAMP_USEC + " "  // timestamp
330                                 + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} "  // walltime
331                                 + "RX "  // direction
332                                 + Pattern.quote("firmware dropped (invalid frame) ")  // fate
333                                 + "Ethernet "  // type
334                                 + "N/A "  // protocol
335                                 + "N/A"  // result
336                 )
337         );
338 
339         // FrameTypeMappings omitted, as they're the same as for TX.
340 
341         for (FateMapping fateMapping : RX_FATE_MAPPINGS) {
342             fateReport = new WifiNative.RxFateReport(
343                     fateMapping.mFateNumber,
344                     FATE_REPORT_DRIVER_TIMESTAMP_USEC,
345                     WifiLoggerHal.FRAME_TYPE_80211_MGMT,
346                     FATE_REPORT_FRAME_BYTES
347             );
348             assertTrue(
349                     fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches(
350                             FATE_REPORT_DRIVER_TIMESTAMP_USEC + " "  // timestamp
351                                     + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} "  // walltime
352                                     + "RX "  // direction
353                                     + Pattern.quote(fateMapping.mExpectedText) + " " // fate
354                                     + "802.11 Mgmt "  // type
355                                     + "N/A " // protocol
356                                     + "N/A"  // result
357                     )
358             );
359         }
360     }
361 
362     /**
363      * Verifies that RxFateReport.toVerboseStringWithPiiAllowed() includes the information we care
364      * about.
365      */
366     @Test
testRxFateReportToVerboseStringWithPiiAllowed()367     public void testRxFateReportToVerboseStringWithPiiAllowed() {
368         WifiNative.RxFateReport fateReport = RX_FATE_REPORT;
369 
370         String verboseFateString = fateReport.toVerboseStringWithPiiAllowed();
371         assertTrue(verboseFateString.contains("Frame direction: RX"));
372         assertTrue(verboseFateString.contains("Frame timestamp: 12345"));
373         assertTrue(verboseFateString.contains("Frame fate: firmware dropped (invalid frame)"));
374         assertTrue(verboseFateString.contains("Frame type: data"));
375         assertTrue(verboseFateString.contains("Frame protocol: Ethernet"));
376         assertTrue(verboseFateString.contains("Frame protocol type: N/A"));
377         assertTrue(verboseFateString.contains("Frame length: 16"));
378         assertTrue(verboseFateString.contains(
379                 "61 62 63 64 65 66 67 68 00 01 02 03 04 05 06 07")); // hex dump
380         // TODO(quiche): uncomment this, once b/27975149 is fixed.
381         // assertTrue(verboseFateString.contains("abcdefgh........"));  // hex dump
382 
383         // FrameTypeMappings omitted, as they're the same as for TX.
384 
385         for (FateMapping fateMapping : RX_FATE_MAPPINGS) {
386             fateReport = new WifiNative.RxFateReport(
387                     fateMapping.mFateNumber,
388                     FATE_REPORT_DRIVER_TIMESTAMP_USEC,
389                     WifiLoggerHal.FRAME_TYPE_80211_MGMT,
390                     FATE_REPORT_FRAME_BYTES
391             );
392             verboseFateString = fateReport.toVerboseStringWithPiiAllowed();
393             assertTrue(verboseFateString.contains("Frame fate: " + fateMapping.mExpectedText));
394         }
395     }
396 
397     /**
398      * Verifies that startPktFateMonitoring returns false when HAL is not started.
399      */
400     @Test
testStartPktFateMonitoringReturnsFalseWhenHalIsNotStarted()401     public void testStartPktFateMonitoringReturnsFalseWhenHalIsNotStarted() {
402         assertFalse(mWifiNative.isHalStarted());
403         assertFalse(mWifiNative.startPktFateMonitoring());
404     }
405 
406     /**
407      * Verifies that getTxPktFates returns error when HAL is not started.
408      */
409     @Test
testGetTxPktFatesReturnsErrorWhenHalIsNotStarted()410     public void testGetTxPktFatesReturnsErrorWhenHalIsNotStarted() {
411         WifiNative.TxFateReport[] fateReports = null;
412         assertFalse(mWifiNative.isHalStarted());
413         assertFalse(mWifiNative.getTxPktFates(fateReports));
414     }
415 
416     /**
417      * Verifies that getRxPktFates returns error when HAL is not started.
418      */
419     @Test
testGetRxPktFatesReturnsErrorWhenHalIsNotStarted()420     public void testGetRxPktFatesReturnsErrorWhenHalIsNotStarted() {
421         WifiNative.RxFateReport[] fateReports = null;
422         assertFalse(mWifiNative.isHalStarted());
423         assertFalse(mWifiNative.getRxPktFates(fateReports));
424     }
425 
426     // TODO(quiche): Add tests for the success cases (when HAL has been started). Specifically:
427     // - testStartPktFateMonitoringCallsHalIfHalIsStarted()
428     // - testGetTxPktFatesCallsHalIfHalIsStarted()
429     // - testGetRxPktFatesCallsHalIfHalIsStarted()
430     //
431     // Adding these tests is difficult to do at the moment, because we can't mock out the HAL
432     // itself. Also, we can't mock out the native methods, because those methods are private.
433     // b/28005116.
434 
435     /** Verifies that getDriverStateDumpNative returns null when HAL is not started. */
436     @Test
testGetDriverStateDumpReturnsNullWhenHalIsNotStarted()437     public void testGetDriverStateDumpReturnsNullWhenHalIsNotStarted() {
438         assertEquals(null, mWifiNative.getDriverStateDump());
439     }
440 
441     // TODO(b/28005116): Add test for the success case of getDriverStateDump().
442 }
443