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