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