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.AdditionalMatchers.gt;
24import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.Matchers.anyString;
26import static org.mockito.Matchers.contains;
27import static org.mockito.Mockito.anyInt;
28import static org.mockito.Mockito.anyObject;
29import static org.mockito.Mockito.doThrow;
30import static org.mockito.Mockito.eq;
31import static org.mockito.Mockito.never;
32import static org.mockito.Mockito.reset;
33import static org.mockito.Mockito.times;
34import static org.mockito.Mockito.verify;
35import static org.mockito.Mockito.when;
36
37import android.app.test.MockAnswerUtil.AnswerWithArguments;
38import android.content.Context;
39import android.support.test.filters.SmallTest;
40
41import com.android.internal.R;
42import com.android.server.am.ActivityManagerService;
43
44import org.junit.Before;
45import org.junit.Ignore;
46import org.junit.Test;
47import org.mockito.Mock;
48import org.mockito.MockitoAnnotations;
49import org.mockito.Spy;
50
51import java.io.ByteArrayInputStream;
52import java.io.FileDescriptor;
53import java.io.PrintWriter;
54import java.io.StringWriter;
55import java.util.regex.Pattern;
56
57/**
58 * Unit tests for {@link WifiDiagnostics}.
59 */
60@SmallTest
61public class WifiDiagnosticsTest {
62    @Mock WifiNative mWifiNative;
63    @Mock BuildProperties mBuildProperties;
64    @Mock Context mContext;
65    @Mock WifiInjector mWifiInjector;
66    @Spy FakeWifiLog mLog;
67    @Mock LastMileLogger mLastMileLogger;
68    @Mock Runtime mJavaRuntime;
69    @Mock Process mExternalProcess;
70    @Mock ActivityManagerService mActivityManagerService;
71    @Mock WifiMetrics mWifiMetrics;
72    WifiDiagnostics mWifiDiagnostics;
73
74    private static final String FAKE_RING_BUFFER_NAME = "fake-ring-buffer";
75    private static final int SMALL_RING_BUFFER_SIZE_KB = 32;
76    private static final int LARGE_RING_BUFFER_SIZE_KB = 1024;
77    private static final int BYTES_PER_KBYTE = 1024;
78    private static final long FAKE_CONNECTION_ID = 1;
79    private static final int ALERT_REASON_CODE = 1;
80    private static final byte[] ALERT_DATA = {0 , 4, 5};
81
82    private WifiNative.RingBufferStatus mFakeRbs;
83    /**
84     * Returns the data that we would dump in a bug report, for our ring buffer.
85     * @return a 2-D byte array, where the first dimension is the record number, and the second
86     * dimension is the byte index within that record.
87     */
88    private final byte[][] getLoggerRingBufferData() throws Exception {
89        return mWifiDiagnostics.getBugReports().get(0).ringBuffers.get(FAKE_RING_BUFFER_NAME);
90    }
91
92    /**
93     * Initializes common state (e.g. mocks) needed by test cases.
94     */
95    @Before
96    public void setUp() throws Exception {
97        MockitoAnnotations.initMocks(this);
98
99        mFakeRbs = new WifiNative.RingBufferStatus();
100        mFakeRbs.name = FAKE_RING_BUFFER_NAME;
101        WifiNative.RingBufferStatus[] ringBufferStatuses = new WifiNative.RingBufferStatus[] {
102                mFakeRbs
103        };
104
105        when(mWifiNative.getRingBufferStatus()).thenReturn(ringBufferStatuses);
106        when(mWifiNative.readKernelLog()).thenReturn("");
107        when(mBuildProperties.isEngBuild()).thenReturn(false);
108        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
109        when(mBuildProperties.isUserBuild()).thenReturn(true);
110        when(mExternalProcess.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[0]));
111        when(mExternalProcess.getErrorStream()).thenReturn(new ByteArrayInputStream(new byte[0]));
112        when(mJavaRuntime.exec(anyString())).thenReturn(mExternalProcess);
113
114        MockResources resources = new MockResources();
115        resources.setInteger(R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb,
116                SMALL_RING_BUFFER_SIZE_KB);
117        resources.setInteger(R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb,
118                LARGE_RING_BUFFER_SIZE_KB);
119        when(mContext.getResources()).thenReturn(resources);
120        when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
121        when(mWifiInjector.getJavaRuntime()).thenReturn(mJavaRuntime);
122        when(mWifiInjector.getActivityManagerService()).thenReturn(mActivityManagerService);
123        when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
124
125        mWifiDiagnostics = new WifiDiagnostics(
126                mContext, mWifiInjector, mWifiNative, mBuildProperties, mLastMileLogger);
127        mWifiNative.enableVerboseLogging(0);
128    }
129
130    /** Verifies that startLogging() registers a logging event handler. */
131    @Test
132    public void startLoggingRegistersLogEventHandler() throws Exception {
133        final boolean verbosityToggle = false;  // even default mode registers handler
134        mWifiDiagnostics.startLogging(verbosityToggle);
135        verify(mWifiNative).setLoggingEventHandler(anyObject());
136    }
137
138    /**
139     * Verifies that a failure to set the logging event handler does not prevent a future
140     * startLogging() from setting the logging event handler.
141     */
142    @Test
143    public void startLoggingRegistersLogEventHandlerIfPriorAttemptFailed()
144            throws Exception {
145        final boolean verbosityToggle = false;  // even default mode registers handler
146
147        when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(false);
148        mWifiDiagnostics.startLogging(verbosityToggle);
149        verify(mWifiNative).setLoggingEventHandler(anyObject());
150        reset(mWifiNative);
151
152        when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
153        mWifiDiagnostics.startLogging(verbosityToggle);
154        verify(mWifiNative).setLoggingEventHandler(anyObject());
155    }
156
157    /** Verifies that startLogging() does not make redundant calls to setLoggingEventHandler(). */
158    @Test
159    public void startLoggingDoesNotRegisterLogEventHandlerIfPriorAttemptSucceeded()
160            throws Exception {
161        final boolean verbosityToggle = false;  // even default mode registers handler
162
163        when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
164        mWifiDiagnostics.startLogging(verbosityToggle);
165        verify(mWifiNative).setLoggingEventHandler(anyObject());
166        reset(mWifiNative);
167
168        mWifiDiagnostics.startLogging(verbosityToggle);
169        verify(mWifiNative, never()).setLoggingEventHandler(anyObject());
170    }
171
172    /**
173     * Verifies that startLogging() restarts HAL ringbuffers.
174     *
175     * Specifically: verifies that startLogging()
176     * a) stops any ring buffer logging that might be already running,
177     * b) instructs WifiNative to enable ring buffers of the appropriate log level.
178     */
179    @Test
180    public void startLoggingStopsAndRestartsRingBufferLoggingInVerboseMode() throws Exception {
181        final boolean verbosityToggle = true;
182        mWifiDiagnostics.startLogging(verbosityToggle);
183        verify(mWifiNative).startLoggingRingBuffer(
184                eq(WifiDiagnostics.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
185                eq(FAKE_RING_BUFFER_NAME));
186        verify(mWifiNative).startLoggingRingBuffer(
187                eq(WifiDiagnostics.VERBOSE_LOG_WITH_WAKEUP), anyInt(), anyInt(), anyInt(),
188                eq(FAKE_RING_BUFFER_NAME));
189    }
190
191    @Test
192    public void startLoggingStopsAndThenStartRingBufferLoggingInNormalMode() throws Exception {
193        final boolean verbosityToggle = false;
194        mWifiDiagnostics.startLogging(verbosityToggle);
195        verify(mWifiNative).startLoggingRingBuffer(
196                eq(WifiDiagnostics.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
197                eq(FAKE_RING_BUFFER_NAME));
198        verify(mWifiNative).startLoggingRingBuffer(
199                gt(WifiDiagnostics.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
200                anyString());
201    }
202
203    /** Verifies that, if a log handler was registered, then stopLogging() resets it. */
204    @Test
205    public void stopLoggingResetsLogHandlerIfHandlerWasRegistered() throws Exception {
206        final boolean verbosityToggle = false;  // even default mode registers handler
207
208        when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
209        mWifiDiagnostics.startLogging(verbosityToggle);
210        reset(mWifiNative);
211
212        mWifiDiagnostics.stopLogging();
213        verify(mWifiNative).resetLogHandler();
214    }
215
216    /** Verifies that, if a log handler is not registered, stopLogging() skips resetLogHandler(). */
217    @Test
218    public void stopLoggingOnlyResetsLogHandlerIfHandlerWasRegistered() throws Exception {
219        mWifiDiagnostics.stopLogging();
220        verify(mWifiNative, never()).resetLogHandler();
221    }
222
223    /** Verifies that stopLogging() remembers that we've reset the log handler. */
224    @Test
225    public void multipleStopLoggingCallsOnlyResetLogHandlerOnce() throws Exception {
226        final boolean verbosityToggle = false;  // even default mode registers handler
227
228        when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
229        mWifiDiagnostics.startLogging(verbosityToggle);
230        reset(mWifiNative);
231
232        when(mWifiNative.resetLogHandler()).thenReturn(true);
233        mWifiDiagnostics.stopLogging();
234        verify(mWifiNative).resetLogHandler();
235        reset(mWifiNative);
236
237        mWifiDiagnostics.stopLogging();
238        verify(mWifiNative, never()).resetLogHandler();
239    }
240
241    /**
242     * Verifies that we capture ring-buffer data.
243     */
244    @Test
245    public void canCaptureAndStoreRingBufferData() throws Exception {
246        final boolean verbosityToggle = false;
247        mWifiDiagnostics.startLogging(verbosityToggle);
248
249        final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
250        mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
251        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
252
253        byte[][] ringBufferData = getLoggerRingBufferData();
254        assertEquals(1, ringBufferData.length);
255        assertArrayEquals(data, ringBufferData[0]);
256    }
257
258    /**
259     * Verifies that we discard extraneous ring-buffer data.
260     */
261    @Ignore("TODO(b/36811399): re-enabled this @Test")
262    @Test
263    public void loggerDiscardsExtraneousData() throws Exception {
264        final boolean verbosityToggle = false;
265        mWifiDiagnostics.startLogging(verbosityToggle);
266
267        final byte[] data1 = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
268        final byte[] data2 = {1, 2, 3};
269        mWifiDiagnostics.onRingBufferData(mFakeRbs, data1);
270        mWifiDiagnostics.onRingBufferData(mFakeRbs, data2);
271        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
272
273        byte[][] ringBufferData = getLoggerRingBufferData();
274        assertEquals(1, ringBufferData.length);
275        assertArrayEquals(data2, ringBufferData[0]);
276    }
277
278    /**
279     * Verifies that, when verbose mode is not enabled, startLogging() calls
280     * startPktFateMonitoring(any()).
281     */
282    @Test
283    public void startLoggingStartsPacketFateWithoutVerboseMode() {
284        final boolean verbosityToggle = false;
285        mWifiDiagnostics.startLogging(verbosityToggle);
286        verify(mWifiNative).startPktFateMonitoring(any());
287    }
288
289    /**
290     * Verifies that, when verbose mode is enabled, startLogging() calls
291     * startPktFateMonitoring(any()).
292     */
293    @Test
294    public void startLoggingStartsPacketFateInVerboseMode() {
295        final boolean verbosityToggle = true;
296        mWifiDiagnostics.startLogging(verbosityToggle);
297        verify(mWifiNative).startPktFateMonitoring(any());
298    }
299
300    // Verifies that startLogging() reports failure of startPktFateMonitoring(any()).
301    @Test
302    public void startLoggingReportsFailureOfStartPktFateMonitoring() {
303        final boolean verbosityToggle = true;
304        when(mWifiNative.startPktFateMonitoring(any())).thenReturn(false);
305        mWifiDiagnostics.startLogging(verbosityToggle);
306        verify(mLog).wC(contains("Failed"));
307    }
308
309    /**
310     * Verifies that, when verbose mode is not enabled,
311     * reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED) still fetches packet fates.
312     */
313    @Test
314    public void reportConnectionFailureIsIgnoredWithoutVerboseMode() {
315        final boolean verbosityToggle = false;
316        mWifiDiagnostics.startLogging(verbosityToggle);
317        mWifiDiagnostics.reportConnectionEvent(
318                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
319        verify(mWifiNative).getTxPktFates(any(), anyObject());
320        verify(mWifiNative).getRxPktFates(any(), anyObject());
321    }
322
323    /**
324     * Verifies that, when verbose mode is enabled, reportConnectionFailure() fetches packet fates.
325     */
326    @Test
327    public void reportConnectionFailureFetchesFatesInVerboseMode() {
328        final boolean verbosityToggle = true;
329        mWifiDiagnostics.startLogging(verbosityToggle);
330        mWifiDiagnostics.reportConnectionEvent(
331                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
332        verify(mWifiNative).getTxPktFates(any(), anyObject());
333        verify(mWifiNative).getRxPktFates(any(), anyObject());
334    }
335
336    @Test
337    public void reportConnectionEventPropagatesStartToLastMileLogger() {
338        final boolean verbosityToggle = false;
339        mWifiDiagnostics.startLogging(verbosityToggle);
340        mWifiDiagnostics.reportConnectionEvent(
341                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_STARTED);
342        verify(mLastMileLogger).reportConnectionEvent(
343                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_STARTED);
344    }
345
346    @Test
347    public void reportConnectionEventPropagatesSuccessToLastMileLogger() {
348        final boolean verbosityToggle = false;
349        mWifiDiagnostics.startLogging(verbosityToggle);
350        mWifiDiagnostics.reportConnectionEvent(
351                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
352        verify(mLastMileLogger).reportConnectionEvent(
353                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
354    }
355
356    @Test
357    public void reportConnectionEventPropagatesFailureToLastMileLogger() {
358        final boolean verbosityToggle = false;
359        mWifiDiagnostics.startLogging(verbosityToggle);
360        mWifiDiagnostics.reportConnectionEvent(
361                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
362        verify(mLastMileLogger).reportConnectionEvent(
363                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
364    }
365
366    /**
367     * Verifies that we try to fetch TX fates, even if fetching RX fates failed.
368     */
369    @Test
370    public void loggerFetchesTxFatesEvenIfFetchingRxFatesFails() {
371        final boolean verbosityToggle = true;
372        when(mWifiNative.getRxPktFates(any(), anyObject())).thenReturn(false);
373        mWifiDiagnostics.startLogging(verbosityToggle);
374        mWifiDiagnostics.reportConnectionEvent(
375                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
376        verify(mWifiNative).getTxPktFates(any(), anyObject());
377        verify(mWifiNative).getRxPktFates(any(), anyObject());
378    }
379
380    /**
381     * Verifies that we try to fetch RX fates, even if fetching TX fates failed.
382     */
383    @Test
384    public void loggerFetchesRxFatesEvenIfFetchingTxFatesFails() {
385        final boolean verbosityToggle = true;
386        when(mWifiNative.getTxPktFates(any(), anyObject())).thenReturn(false);
387        mWifiDiagnostics.startLogging(verbosityToggle);
388        mWifiDiagnostics.reportConnectionEvent(
389                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
390        verify(mWifiNative).getTxPktFates(any(), anyObject());
391        verify(mWifiNative).getRxPktFates(any(), anyObject());
392    }
393
394    /** Verifies that dump() fetches the latest fates. */
395    @Test
396    public void dumpFetchesFates() {
397        final boolean verbosityToggle = false;
398        StringWriter sw = new StringWriter();
399        PrintWriter pw = new PrintWriter(sw);
400        mWifiDiagnostics.startLogging(verbosityToggle);
401        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
402        verify(mWifiNative).getTxPktFates(any(), anyObject());
403        verify(mWifiNative).getRxPktFates(any(), anyObject());
404    }
405
406    /**
407     * Verifies that dump() doesn't crash, or generate garbage, in the case where we haven't fetched
408     * any fates.
409     */
410    @Test
411    public void dumpSucceedsWhenNoFatesHaveNotBeenFetched() {
412        StringWriter sw = new StringWriter();
413        PrintWriter pw = new PrintWriter(sw);
414        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
415
416        String fateDumpString = sw.toString();
417        assertTrue(fateDumpString.contains("Last failed"));
418        // Verify dump terminator is present
419        assertTrue(fateDumpString.contains(
420                "--------------------------------------------------------------------"));
421    }
422
423    /**
424     * Verifies that dump() doesn't crash, or generate garbage, in the case where the fates that
425     * the HAL-provided fates are empty.
426     */
427    @Test
428    public void dumpSucceedsWhenFatesHaveBeenFetchedButAreEmpty() {
429        final boolean verbosityToggle = true;
430        mWifiDiagnostics.startLogging(verbosityToggle);
431        mWifiDiagnostics.reportConnectionEvent(
432                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
433        verify(mWifiNative).getTxPktFates(any(), anyObject());
434        verify(mWifiNative).getRxPktFates(any(), anyObject());
435
436        StringWriter sw = new StringWriter();
437        PrintWriter pw = new PrintWriter(sw);
438        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
439
440        String fateDumpString = sw.toString();
441        assertTrue(fateDumpString.contains("Last failed"));
442        // Verify dump terminator is present
443        assertTrue(fateDumpString.contains(
444                "--------------------------------------------------------------------"));
445    }
446
447    private String getDumpString(boolean verbose) {
448        mWifiDiagnostics.startLogging(verbose);
449        mWifiNative.enableVerboseLogging(verbose ? 1 : 0);
450        when(mWifiNative.getTxPktFates(any(), anyObject())).then(new AnswerWithArguments() {
451            public boolean answer(String ifaceName, WifiNative.TxFateReport[] fates) {
452                fates[0] = new WifiNative.TxFateReport(
453                        WifiLoggerHal.TX_PKT_FATE_ACKED, 2, WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
454                        new byte[0]
455                );
456                fates[1] = new WifiNative.TxFateReport(
457                        WifiLoggerHal.TX_PKT_FATE_ACKED, 0, WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
458                        new byte[0]
459                );
460                return true;
461            }
462        });
463        when(mWifiNative.getRxPktFates(any(), anyObject())).then(new AnswerWithArguments() {
464            public boolean answer(String ifaceName, WifiNative.RxFateReport[] fates) {
465                fates[0] = new WifiNative.RxFateReport(
466                        WifiLoggerHal.RX_PKT_FATE_SUCCESS, 3, WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
467                        new byte[0]
468                );
469                fates[1] = new WifiNative.RxFateReport(
470                        WifiLoggerHal.RX_PKT_FATE_SUCCESS, 1, WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
471                        new byte[0]
472                );
473                return true;
474            }
475        });
476        mWifiDiagnostics.reportConnectionEvent(
477                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
478
479        StringWriter sw = new StringWriter();
480        PrintWriter pw = new PrintWriter(sw);
481        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
482        return sw.toString();
483    }
484
485      /**
486     * Verifies that dump() shows both TX, and RX fates in only table form, when verbose
487     * logging is not enabled.
488     */
489    @Test
490    public void dumpShowsTxAndRxFates() {
491        final boolean verbosityToggle = false;
492        String dumpString = getDumpString(verbosityToggle);
493        assertTrue(dumpString.contains(WifiNative.FateReport.getTableHeader()));
494        assertTrue(Pattern.compile("0 .* TX ").matcher(dumpString).find());
495        assertTrue(Pattern.compile("1 .* RX ").matcher(dumpString).find());
496        assertTrue(Pattern.compile("2 .* TX ").matcher(dumpString).find());
497        assertTrue(Pattern.compile("3 .* RX ").matcher(dumpString).find());
498        assertFalse(dumpString.contains("VERBOSE PACKET FATE DUMP"));
499        assertFalse(dumpString.contains("Frame bytes"));
500    }
501
502    /**
503     * Verifies that dump() shows both TX, and RX fates in table and verbose forms, when verbose
504     * logging is enabled.
505     */
506    @Test
507    public void dumpShowsTxAndRxFatesVerbose() {
508        final boolean verbosityToggle = true;
509        String dumpString = getDumpString(verbosityToggle);
510        assertTrue(dumpString.contains(WifiNative.FateReport.getTableHeader()));
511        assertTrue(Pattern.compile("0 .* TX ").matcher(dumpString).find());
512        assertTrue(Pattern.compile("1 .* RX ").matcher(dumpString).find());
513        assertTrue(Pattern.compile("2 .* TX ").matcher(dumpString).find());
514        assertTrue(Pattern.compile("3 .* RX ").matcher(dumpString).find());
515        assertTrue(dumpString.contains("VERBOSE PACKET FATE DUMP"));
516        assertTrue(dumpString.contains("Frame bytes"));
517    }
518
519    /**
520     * Verifies that dump() outputs frames in timestamp order, even though the HAL provided the
521     * data out-of-order (order is specified in getDumpString()).
522     */
523    @Test
524    public void dumpIsSortedByTimestamp() {
525        final boolean verbosityToggle = true;
526        String dumpString = getDumpString(verbosityToggle);
527        assertTrue(dumpString.contains(WifiNative.FateReport.getTableHeader()));
528        assertTrue(Pattern.compile(
529                "0 .* TX .*\n" +
530                "1 .* RX .*\n" +
531                "2 .* TX .*\n" +
532                "3 .* RX "
533        ).matcher(dumpString).find());
534
535        int expected_index_of_verbose_frame_0 = dumpString.indexOf(
536                "Frame direction: TX\nFrame timestamp: 0\n");
537        int expected_index_of_verbose_frame_1 = dumpString.indexOf(
538                "Frame direction: RX\nFrame timestamp: 1\n");
539        int expected_index_of_verbose_frame_2 = dumpString.indexOf(
540                "Frame direction: TX\nFrame timestamp: 2\n");
541        int expected_index_of_verbose_frame_3 = dumpString.indexOf(
542                "Frame direction: RX\nFrame timestamp: 3\n");
543        assertFalse(-1 == expected_index_of_verbose_frame_0);
544        assertFalse(-1 == expected_index_of_verbose_frame_1);
545        assertFalse(-1 == expected_index_of_verbose_frame_2);
546        assertFalse(-1 == expected_index_of_verbose_frame_3);
547        assertTrue(expected_index_of_verbose_frame_0 < expected_index_of_verbose_frame_1);
548        assertTrue(expected_index_of_verbose_frame_1 < expected_index_of_verbose_frame_2);
549        assertTrue(expected_index_of_verbose_frame_2 < expected_index_of_verbose_frame_3);
550    }
551
552    /** Verifies that eng builds do not show fate detail outside of verbose mode. */
553    @Test
554    public void dumpOmitsFateDetailInEngBuildsOutsideOfVerboseMode() throws Exception {
555        final boolean verbosityToggle = false;
556        when(mBuildProperties.isEngBuild()).thenReturn(true);
557        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
558        when(mBuildProperties.isUserBuild()).thenReturn(false);
559        String dumpString = getDumpString(verbosityToggle);
560        assertFalse(dumpString.contains("VERBOSE PACKET FATE DUMP"));
561        assertFalse(dumpString.contains("Frame bytes"));
562    }
563
564    /** Verifies that userdebug builds do not show fate detail outside of verbose mode. */
565    @Test
566    public void dumpOmitsFateDetailInUserdebugBuildsOutsideOfVerboseMode() throws Exception {
567        final boolean verbosityToggle = false;
568        when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
569        when(mBuildProperties.isEngBuild()).thenReturn(false);
570        when(mBuildProperties.isUserBuild()).thenReturn(false);
571        String dumpString = getDumpString(verbosityToggle);
572        assertFalse(dumpString.contains("VERBOSE PACKET FATE DUMP"));
573        assertFalse(dumpString.contains("Frame bytes"));
574    }
575
576    /**
577     * Verifies that, if verbose is disabled after fetching fates, the dump does not include
578     * verbose fate logs.
579     */
580    @Test
581    public void dumpOmitsFatesIfVerboseIsDisabledAfterFetch() {
582        final boolean verbosityToggle = true;
583        mWifiDiagnostics.startLogging(verbosityToggle);
584        when(mWifiNative.getTxPktFates(any(), anyObject())).then(new AnswerWithArguments() {
585            public boolean answer(String ifaceName, WifiNative.TxFateReport[] fates) {
586                fates[0] = new WifiNative.TxFateReport(
587                        WifiLoggerHal.TX_PKT_FATE_ACKED, 0, WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
588                        new byte[0]
589                );
590                return true;
591            }
592        });
593        when(mWifiNative.getRxPktFates(any(), anyObject())).then(new AnswerWithArguments() {
594            public boolean answer(String ifaceName, WifiNative.RxFateReport[] fates) {
595                fates[0] = new WifiNative.RxFateReport(
596                        WifiLoggerHal.RX_PKT_FATE_SUCCESS, 1, WifiLoggerHal.FRAME_TYPE_ETHERNET_II,
597                        new byte[0]
598                );
599                return true;
600            }
601        });
602        mWifiDiagnostics.reportConnectionEvent(
603                FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
604        verify(mWifiNative).getTxPktFates(any(), anyObject());
605        verify(mWifiNative).getRxPktFates(any(), anyObject());
606
607        final boolean newVerbosityToggle = false;
608        mWifiDiagnostics.startLogging(newVerbosityToggle);
609
610        StringWriter sw = new StringWriter();
611        PrintWriter pw = new PrintWriter(sw);
612        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
613
614        String fateDumpString = sw.toString();
615        assertFalse(fateDumpString.contains("VERBOSE PACKET FATE DUMP"));
616        assertFalse(fateDumpString.contains("Frame bytes"));
617    }
618
619    /** Verifies that the default size of our ring buffers is small. */
620    @Ignore("TODO(b/36811399): re-enable this @Test")
621    @Test
622    public void ringBufferSizeIsSmallByDefault() throws Exception {
623        final boolean verbosityToggle = false;
624        mWifiDiagnostics.startLogging(verbosityToggle);
625        mWifiDiagnostics.onRingBufferData(
626                mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
627        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
628        assertEquals(0, getLoggerRingBufferData().length);
629    }
630
631    /** Verifies that we use small ring buffers by default, on userdebug builds. */
632    @Ignore("TODO(b/36811399): re-enable this @Test")
633    @Test
634    public void ringBufferSizeIsSmallByDefaultOnUserdebugBuilds() throws Exception {
635        final boolean verbosityToggle = false;
636        when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
637        when(mBuildProperties.isEngBuild()).thenReturn(false);
638        when(mBuildProperties.isUserBuild()).thenReturn(false);
639        mWifiDiagnostics.startLogging(verbosityToggle);
640        mWifiDiagnostics.onRingBufferData(
641                mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
642        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
643        assertEquals(0, getLoggerRingBufferData().length);
644    }
645
646    /** Verifies that we use small ring buffers by default, on eng builds. */
647    @Ignore("TODO(b/36811399): re-enable this @Test")
648    @Test
649    public void ringBufferSizeIsSmallByDefaultOnEngBuilds() throws Exception {
650        final boolean verbosityToggle = false;
651        when(mBuildProperties.isEngBuild()).thenReturn(true);
652        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
653        when(mBuildProperties.isUserBuild()).thenReturn(false);
654        mWifiDiagnostics.startLogging(verbosityToggle);
655        mWifiDiagnostics.onRingBufferData(
656                mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
657        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
658        assertEquals(0, getLoggerRingBufferData().length);
659    }
660
661    /** Verifies that we use large ring buffers when initially started in verbose mode. */
662    @Test
663    public void ringBufferSizeIsLargeInVerboseMode() throws Exception {
664        final boolean verbosityToggle = true;
665
666        mWifiDiagnostics.startLogging(verbosityToggle);
667        mWifiDiagnostics.onRingBufferData(
668                mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]);
669        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
670        assertEquals(1, getLoggerRingBufferData().length);
671    }
672
673    /** Verifies that we use large ring buffers when switched from normal to verbose mode. */
674    @Test
675    public void startLoggingGrowsRingBuffersIfNeeded() throws Exception {
676        mWifiDiagnostics.startLogging(false  /* verbose disabled */);
677        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
678        mWifiDiagnostics.onRingBufferData(
679                mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]);
680        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
681        assertEquals(1, getLoggerRingBufferData().length);
682    }
683
684    /** Verifies that we use small ring buffers when switched from verbose to normal mode. */
685    @Ignore("TODO(b/36811399): re-enabled this @Test")
686    @Test
687    public void startLoggingShrinksRingBuffersIfNeeded() throws Exception {
688
689        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
690        mWifiDiagnostics.onRingBufferData(
691                mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
692
693        // Existing data is nuked (too large).
694        mWifiDiagnostics.startLogging(false  /* verbose disabled */);
695        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
696        assertEquals(0, getLoggerRingBufferData().length);
697
698        // New data must obey limit as well.
699        mWifiDiagnostics.onRingBufferData(
700                mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
701        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
702        assertEquals(0, getLoggerRingBufferData().length);
703    }
704
705    /**
706     * Verifies that we capture a bugreport & store alert data when WifiNative invokes
707     * the alert callback.
708     */
709    @Test
710    public void onWifiAlertCapturesBugreportAndIncrementsMetrics() throws Exception {
711        mWifiDiagnostics.onWifiAlert(ALERT_REASON_CODE, ALERT_DATA);
712
713        assertEquals(1, mWifiDiagnostics.getAlertReports().size());
714        WifiDiagnostics.BugReport alertReport = mWifiDiagnostics.getAlertReports().get(0);
715        assertEquals(ALERT_REASON_CODE, alertReport.errorCode);
716        assertArrayEquals(ALERT_DATA, alertReport.alertData);
717
718        verify(mWifiMetrics).incrementAlertReasonCount(ALERT_REASON_CODE);
719    }
720
721    /** Verifies that we skip the firmware and driver dumps if verbose is not enabled. */
722    @Test
723    public void captureBugReportSkipsFirmwareAndDriverDumpsByDefault() {
724        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
725        verify(mWifiNative, never()).getFwMemoryDump();
726        verify(mWifiNative, never()).getDriverStateDump();
727    }
728
729    /** Verifies that we capture the firmware and driver dumps if verbose is enabled. */
730    @Test
731    public void captureBugReportTakesFirmwareAndDriverDumpsInVerboseMode() {
732        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
733        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
734        verify(mWifiNative).getFwMemoryDump();
735        verify(mWifiNative).getDriverStateDump();
736    }
737
738    /** Verifies that the dump includes driver state, if driver state was provided by HAL. */
739    @Test
740    public void dumpIncludesDriverStateDumpIfAvailable() {
741        when(mWifiNative.getDriverStateDump()).thenReturn(new byte[]{0, 1, 2});
742
743        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
744        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
745        verify(mWifiNative).getDriverStateDump();
746
747        StringWriter sw = new StringWriter();
748        PrintWriter pw = new PrintWriter(sw);
749        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
750        assertTrue(sw.toString().contains(WifiDiagnostics.DRIVER_DUMP_SECTION_HEADER));
751    }
752
753    /** Verifies that the dump skips driver state, if driver state was not provided by HAL. */
754    @Test
755    public void dumpOmitsDriverStateDumpIfUnavailable() {
756        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
757        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
758        verify(mWifiNative).getDriverStateDump();
759
760        StringWriter sw = new StringWriter();
761        PrintWriter pw = new PrintWriter(sw);
762        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
763        assertFalse(sw.toString().contains(WifiDiagnostics.DRIVER_DUMP_SECTION_HEADER));
764    }
765
766    /** Verifies that the dump omits driver state, if verbose was disabled after capture. */
767    @Test
768    public void dumpOmitsDriverStateDumpIfVerboseDisabledAfterCapture() {
769        when(mWifiNative.getDriverStateDump()).thenReturn(new byte[]{0, 1, 2});
770
771        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
772        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
773        verify(mWifiNative).getDriverStateDump();
774
775        mWifiDiagnostics.startLogging(false  /* verbose no longer enabled */);
776
777        StringWriter sw = new StringWriter();
778        PrintWriter pw = new PrintWriter(sw);
779        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
780        assertFalse(sw.toString().contains(WifiDiagnostics.DRIVER_DUMP_SECTION_HEADER));
781    }
782
783    /** Verifies that the dump includes firmware dump, if firmware dump was provided by HAL. */
784    @Test
785    public void dumpIncludesFirmwareMemoryDumpIfAvailable() {
786        when(mWifiNative.getFwMemoryDump()).thenReturn(new byte[]{0, 1, 2});
787
788        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
789        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
790        verify(mWifiNative).getFwMemoryDump();
791
792        StringWriter sw = new StringWriter();
793        PrintWriter pw = new PrintWriter(sw);
794        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
795        assertTrue(sw.toString().contains(WifiDiagnostics.FIRMWARE_DUMP_SECTION_HEADER));
796    }
797
798    /** Verifies that the dump skips firmware memory, if firmware memory was not provided by HAL. */
799    @Test
800    public void dumpOmitsFirmwareMemoryDumpIfUnavailable() {
801        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
802        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
803        verify(mWifiNative).getFwMemoryDump();
804
805        StringWriter sw = new StringWriter();
806        PrintWriter pw = new PrintWriter(sw);
807        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
808        assertFalse(sw.toString().contains(WifiDiagnostics.FIRMWARE_DUMP_SECTION_HEADER));
809    }
810
811    /** Verifies that the dump omits firmware memory, if verbose was disabled after capture. */
812    @Test
813    public void dumpOmitsFirmwareMemoryDumpIfVerboseDisabledAfterCapture() {
814        when(mWifiNative.getFwMemoryDump()).thenReturn(new byte[]{0, 1, 2});
815
816        mWifiDiagnostics.startLogging(true  /* verbose enabled */);
817        mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
818        verify(mWifiNative).getFwMemoryDump();
819
820        mWifiDiagnostics.startLogging(false  /* verbose no longer enabled */);
821
822        StringWriter sw = new StringWriter();
823        PrintWriter pw = new PrintWriter(sw);
824        mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
825        assertFalse(sw.toString().contains(WifiDiagnostics.FIRMWARE_DUMP_SECTION_HEADER));
826    }
827
828    @Test
829    public void dumpRequestsLastMileLoggerDump() {
830        mWifiDiagnostics.dump(
831                new FileDescriptor(), new PrintWriter(new StringWriter()), new String[]{});
832        verify(mLastMileLogger).dump(anyObject());
833    }
834
835    @Test
836    public void takeBugReportCallsActivityManagerOnUserDebug() {
837        when(mBuildProperties.isUserBuild()).thenReturn(false);
838        mWifiDiagnostics.takeBugReport("", "");
839        verify(mActivityManagerService, times(1)).requestWifiBugReport(
840                anyString(), anyString());
841    }
842
843    @Test
844    public void takeBugReportSwallowsExceptions() {
845        when(mBuildProperties.isUserBuild()).thenReturn(false);
846        doThrow(new RuntimeException()).when(mActivityManagerService).requestWifiBugReport(
847                anyString(), anyString());
848        mWifiDiagnostics.takeBugReport("", "");
849        verify(mActivityManagerService, times(1)).requestWifiBugReport(
850                anyString(), anyString());
851    }
852
853    @Test
854    public void takeBugReportDoesNothingOnUserBuild() {
855        when(mBuildProperties.isUserBuild()).thenReturn(true);
856        mWifiDiagnostics.takeBugReport("", "");
857        verify(mActivityManagerService, never()).requestWifiBugReport(anyString(), anyString());
858    }
859}
860