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
17package com.android.server.wifi;
18
19import static org.junit.Assert.assertArrayEquals;
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertTrue;
23import static org.mockito.Mockito.anyInt;
24import static org.mockito.Mockito.anyString;
25import static org.mockito.Mockito.spy;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.test.suitebuilder.annotation.SmallTest;
30
31import org.junit.Before;
32import org.junit.Test;
33
34import java.lang.reflect.Constructor;
35import java.util.HashMap;
36import java.util.Map;
37import java.util.regex.Pattern;
38
39/**
40 * Unit tests for {@link com.android.server.wifi.WifiNative}.
41 */
42@SmallTest
43public 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
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
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
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
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
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;
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;
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
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
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
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
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
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
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
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
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
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