1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.util.ArraySet; 22 import android.util.Base64; 23 import android.util.Log; 24 import android.util.SparseLongArray; 25 26 import com.android.internal.R; 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.server.wifi.util.ByteArrayRingBuffer; 29 import com.android.server.wifi.util.StringUtil; 30 31 import java.io.BufferedReader; 32 import java.io.ByteArrayOutputStream; 33 import java.io.FileDescriptor; 34 import java.io.IOException; 35 import java.io.InputStreamReader; 36 import java.io.PrintWriter; 37 import java.nio.charset.Charset; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Calendar; 41 import java.util.Collections; 42 import java.util.Comparator; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Set; 46 import java.util.stream.Collectors; 47 import java.util.zip.Deflater; 48 49 /** 50 * Tracks various logs for framework. 51 */ 52 class WifiDiagnostics extends BaseWifiDiagnostics { 53 /** 54 * Thread-safety: 55 * 1) All non-private methods are |synchronized|. 56 * 2) Callbacks into WifiDiagnostics use non-private (and hence, synchronized) methods. See, e.g, 57 * onRingBufferData(), onWifiAlert(). 58 */ 59 60 private static final String TAG = "WifiDiags"; 61 private static final boolean DBG = false; 62 63 /** log level flags; keep these consistent with wifi_logger.h */ 64 65 /** No logs whatsoever */ 66 public static final int VERBOSE_NO_LOG = 0; 67 /** No logs whatsoever */ 68 public static final int VERBOSE_NORMAL_LOG = 1; 69 /** Be careful since this one can affect performance and power */ 70 public static final int VERBOSE_LOG_WITH_WAKEUP = 2; 71 /** Be careful since this one can affect performance and power and memory */ 72 public static final int VERBOSE_DETAILED_LOG_WITH_WAKEUP = 3; 73 74 /** ring buffer flags; keep these consistent with wifi_logger.h */ 75 public static final int RING_BUFFER_FLAG_HAS_BINARY_ENTRIES = 0x00000001; 76 public static final int RING_BUFFER_FLAG_HAS_ASCII_ENTRIES = 0x00000002; 77 public static final int RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES = 0x00000004; 78 79 /** various reason codes */ 80 public static final int REPORT_REASON_NONE = 0; 81 public static final int REPORT_REASON_ASSOC_FAILURE = 1; 82 public static final int REPORT_REASON_AUTH_FAILURE = 2; 83 public static final int REPORT_REASON_AUTOROAM_FAILURE = 3; 84 public static final int REPORT_REASON_DHCP_FAILURE = 4; 85 public static final int REPORT_REASON_UNEXPECTED_DISCONNECT = 5; 86 public static final int REPORT_REASON_SCAN_FAILURE = 6; 87 public static final int REPORT_REASON_USER_ACTION = 7; 88 public static final int REPORT_REASON_WIFINATIVE_FAILURE = 8; 89 public static final int REPORT_REASON_REACHABILITY_LOST = 9; 90 public static final int REPORT_REASON_FATAL_FW_ALERT = 10; 91 92 /** number of bug reports to hold */ 93 public static final int MAX_BUG_REPORTS = 4; 94 95 /** number of alerts to hold */ 96 public static final int MAX_ALERT_REPORTS = 1; 97 98 /** minimum wakeup interval for each of the log levels */ 99 private static final int MinWakeupIntervals[] = new int[] { 0, 3600, 60, 10 }; 100 /** minimum buffer size for each of the log levels */ 101 private static final int MinBufferSizes[] = new int[] { 0, 16384, 16384, 65536 }; 102 103 /** Map from dump reason to elapsed time millis */ 104 private final SparseLongArray mLastDumpTime = new SparseLongArray(); 105 106 /** Minimum dump period with same error code */ 107 public static final long MIN_DUMP_TIME_WINDOW_MILLIS = 10 * 60 * 1000; // 10 mins 108 109 @VisibleForTesting public static final String FIRMWARE_DUMP_SECTION_HEADER = 110 "FW Memory dump"; 111 @VisibleForTesting public static final String DRIVER_DUMP_SECTION_HEADER = 112 "Driver state dump"; 113 114 private final int RING_BUFFER_BYTE_LIMIT_SMALL; 115 private final int RING_BUFFER_BYTE_LIMIT_LARGE; 116 private int mLogLevel = VERBOSE_NO_LOG; 117 private boolean mIsLoggingEventHandlerRegistered; 118 private WifiNative.RingBufferStatus[] mRingBuffers; 119 private WifiNative.RingBufferStatus mPerPacketRingBuffer; 120 private final BuildProperties mBuildProperties; 121 private final WifiLog mLog; 122 private final LastMileLogger mLastMileLogger; 123 private final Runtime mJavaRuntime; 124 private final WifiMetrics mWifiMetrics; 125 private int mMaxRingBufferSizeBytes; 126 private final List<Integer> mFatalFirmwareAlertErrorCodeList; 127 private WifiInjector mWifiInjector; 128 private Clock mClock; 129 130 /** Interfaces started logging */ 131 private final Set<String> mActiveInterfaces = new ArraySet<>(); 132 WifiDiagnostics(Context context, WifiInjector wifiInjector, WifiNative wifiNative, BuildProperties buildProperties, LastMileLogger lastMileLogger, Clock clock)133 public WifiDiagnostics(Context context, WifiInjector wifiInjector, 134 WifiNative wifiNative, BuildProperties buildProperties, 135 LastMileLogger lastMileLogger, Clock clock) { 136 super(wifiNative); 137 RING_BUFFER_BYTE_LIMIT_SMALL = context.getResources().getInteger( 138 R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb) * 1024; 139 RING_BUFFER_BYTE_LIMIT_LARGE = context.getResources().getInteger( 140 R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb) * 1024; 141 int[] fatalFirmwareAlertErrorCodeArray = context.getResources().getIntArray( 142 R.array.config_wifi_fatal_firmware_alert_error_code_list); 143 144 mBuildProperties = buildProperties; 145 mIsLoggingEventHandlerRegistered = false; 146 mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL; 147 mFatalFirmwareAlertErrorCodeList = Arrays.stream(fatalFirmwareAlertErrorCodeArray) 148 .boxed().collect(Collectors.toList()); 149 mLog = wifiInjector.makeLog(TAG); 150 mLastMileLogger = lastMileLogger; 151 mJavaRuntime = wifiInjector.getJavaRuntime(); 152 mWifiMetrics = wifiInjector.getWifiMetrics(); 153 mWifiInjector = wifiInjector; 154 mClock = clock; 155 } 156 157 /** 158 * Start wifi HAL dependent logging features. 159 * This method should be called only after the interface has 160 * been set up. 161 * 162 * @param ifaceName the interface requesting to start logging. 163 */ 164 @Override startLogging(@onNull String ifaceName)165 public synchronized void startLogging(@NonNull String ifaceName) { 166 if (mActiveInterfaces.contains(ifaceName)) { 167 Log.w(TAG, "Interface: " + ifaceName + " had already started logging"); 168 return; 169 } 170 if (mActiveInterfaces.isEmpty()) { 171 mFirmwareVersion = mWifiNative.getFirmwareVersion(); 172 mDriverVersion = mWifiNative.getDriverVersion(); 173 mSupportedFeatureSet = mWifiNative.getSupportedLoggerFeatureSet(); 174 175 if (!mIsLoggingEventHandlerRegistered) { 176 mIsLoggingEventHandlerRegistered = mWifiNative.setLoggingEventHandler(mHandler); 177 } 178 179 startLoggingRingBuffers(); 180 } 181 182 mActiveInterfaces.add(ifaceName); 183 184 Log.d(TAG, "startLogging() iface list is " + mActiveInterfaces 185 + " after adding " + ifaceName); 186 } 187 188 @Override startPacketLog()189 public synchronized void startPacketLog() { 190 if (mPerPacketRingBuffer != null) { 191 startLoggingRingBuffer(mPerPacketRingBuffer); 192 } else { 193 if (DBG) mLog.tC("There is no per packet ring buffer"); 194 } 195 } 196 197 @Override stopPacketLog()198 public synchronized void stopPacketLog() { 199 if (mPerPacketRingBuffer != null) { 200 stopLoggingRingBuffer(mPerPacketRingBuffer); 201 } else { 202 if (DBG) mLog.tC("There is no per packet ring buffer"); 203 } 204 } 205 206 /** 207 * Stop wifi HAL dependent logging features. 208 * This method should be called before the interface has been 209 * torn down. 210 * 211 * @param ifaceName the interface requesting to stop logging. 212 */ 213 @Override stopLogging(@onNull String ifaceName)214 public synchronized void stopLogging(@NonNull String ifaceName) { 215 if (!mActiveInterfaces.contains(ifaceName)) { 216 Log.w(TAG, "ifaceName: " + ifaceName + " is not in the start log user list"); 217 return; 218 } 219 220 mActiveInterfaces.remove(ifaceName); 221 222 Log.d(TAG, "stopLogging() iface list is " + mActiveInterfaces 223 + " after removing " + ifaceName); 224 225 if (!mActiveInterfaces.isEmpty()) { 226 return; 227 } 228 if (mIsLoggingEventHandlerRegistered) { 229 if (!mWifiNative.resetLogHandler()) { 230 mLog.wC("Fail to reset log handler"); 231 } else { 232 if (DBG) mLog.tC("Reset log handler"); 233 } 234 // Clear mIsLoggingEventHandlerRegistered even if resetLogHandler() failed, because 235 // the log handler is in an indeterminate state. 236 mIsLoggingEventHandlerRegistered = false; 237 } 238 if (mLogLevel != VERBOSE_NO_LOG) { 239 stopLoggingAllBuffers(); 240 mRingBuffers = null; 241 } 242 } 243 244 @Override reportConnectionEvent(byte event)245 public synchronized void reportConnectionEvent(byte event) { 246 mLastMileLogger.reportConnectionEvent(event); 247 if (event == CONNECTION_EVENT_FAILED || event == CONNECTION_EVENT_TIMEOUT) { 248 mPacketFatesForLastFailure = fetchPacketFates(); 249 } 250 } 251 252 @Override captureBugReportData(int reason)253 public synchronized void captureBugReportData(int reason) { 254 BugReport report = captureBugreport(reason, isVerboseLoggingEnabled()); 255 mLastBugReports.addLast(report); 256 flushDump(reason); 257 } 258 259 @Override captureAlertData(int errorCode, byte[] alertData)260 public synchronized void captureAlertData(int errorCode, byte[] alertData) { 261 BugReport report = captureBugreport(errorCode, isVerboseLoggingEnabled()); 262 report.alertData = alertData; 263 mLastAlerts.addLast(report); 264 /* Flush HAL ring buffer when detecting data stall */ 265 if (mFatalFirmwareAlertErrorCodeList.contains(errorCode)) { 266 flushDump(REPORT_REASON_FATAL_FW_ALERT); 267 } 268 } 269 270 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)271 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 272 super.dump(pw); 273 274 for (int i = 0; i < mLastAlerts.size(); i++) { 275 pw.println("--------------------------------------------------------------------"); 276 pw.println("Alert dump " + i); 277 pw.print(mLastAlerts.get(i)); 278 pw.println("--------------------------------------------------------------------"); 279 } 280 281 for (int i = 0; i < mLastBugReports.size(); i++) { 282 pw.println("--------------------------------------------------------------------"); 283 pw.println("Bug dump " + i); 284 pw.print(mLastBugReports.get(i)); 285 pw.println("--------------------------------------------------------------------"); 286 } 287 288 pw.println("Last Flush Time: " + mLastDumpTime.toString()); 289 pw.println("--------------------------------------------------------------------"); 290 291 dumpPacketFates(pw); 292 mLastMileLogger.dump(pw); 293 294 pw.println("--------------------------------------------------------------------"); 295 } 296 297 @Override 298 /** 299 * Initiates a system-level bugreport, in a non-blocking fashion. 300 */ takeBugReport(String bugTitle, String bugDetail)301 public void takeBugReport(String bugTitle, String bugDetail) { 302 if (mBuildProperties.isUserBuild()) { 303 return; 304 } 305 306 try { 307 mWifiInjector.getActivityManagerService().requestWifiBugReport( 308 bugTitle, bugDetail); 309 } catch (Exception e) { // diagnostics should never crash system_server 310 mLog.err("error taking bugreport: %").c(e.getClass().getName()).flush(); 311 } 312 } 313 314 /* private methods and data */ 315 class BugReport { 316 long systemTimeMs; 317 long kernelTimeNanos; 318 int errorCode; 319 HashMap<String, byte[][]> ringBuffers = new HashMap(); 320 byte[] fwMemoryDump; 321 byte[] mDriverStateDump; 322 byte[] alertData; 323 LimitedCircularArray<String> kernelLogLines; 324 ArrayList<String> logcatLines; 325 clearVerboseLogs()326 void clearVerboseLogs() { 327 fwMemoryDump = null; 328 mDriverStateDump = null; 329 } 330 toString()331 public String toString() { 332 StringBuilder builder = new StringBuilder(); 333 334 Calendar c = Calendar.getInstance(); 335 c.setTimeInMillis(systemTimeMs); 336 builder.append("system time = ").append( 337 String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)).append("\n"); 338 339 long kernelTimeMs = kernelTimeNanos/(1000*1000); 340 builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append 341 (kernelTimeMs%1000).append("\n"); 342 343 if (alertData == null) 344 builder.append("reason = ").append(errorCode).append("\n"); 345 else { 346 builder.append("errorCode = ").append(errorCode); 347 builder.append("data \n"); 348 builder.append(compressToBase64(alertData)).append("\n"); 349 } 350 351 if (kernelLogLines != null) { 352 builder.append("kernel log: \n"); 353 for (int i = 0; i < kernelLogLines.size(); i++) { 354 builder.append(kernelLogLines.get(i)).append("\n"); 355 } 356 builder.append("\n"); 357 } 358 359 if (logcatLines != null) { 360 builder.append("system log: \n"); 361 for (int i = 0; i < logcatLines.size(); i++) { 362 builder.append(logcatLines.get(i)).append("\n"); 363 } 364 builder.append("\n"); 365 } 366 367 for (HashMap.Entry<String, byte[][]> e : ringBuffers.entrySet()) { 368 String ringName = e.getKey(); 369 byte[][] buffers = e.getValue(); 370 builder.append("ring-buffer = ").append(ringName).append("\n"); 371 372 int size = 0; 373 for (int i = 0; i < buffers.length; i++) { 374 size += buffers[i].length; 375 } 376 377 byte[] buffer = new byte[size]; 378 int index = 0; 379 for (int i = 0; i < buffers.length; i++) { 380 System.arraycopy(buffers[i], 0, buffer, index, buffers[i].length); 381 index += buffers[i].length; 382 } 383 384 builder.append(compressToBase64(buffer)); 385 builder.append("\n"); 386 } 387 388 if (fwMemoryDump != null) { 389 builder.append(FIRMWARE_DUMP_SECTION_HEADER); 390 builder.append("\n"); 391 builder.append(compressToBase64(fwMemoryDump)); 392 builder.append("\n"); 393 } 394 395 if (mDriverStateDump != null) { 396 builder.append(DRIVER_DUMP_SECTION_HEADER); 397 if (StringUtil.isAsciiPrintable(mDriverStateDump)) { 398 builder.append(" (ascii)\n"); 399 builder.append(new String(mDriverStateDump, Charset.forName("US-ASCII"))); 400 builder.append("\n"); 401 } else { 402 builder.append(" (base64)\n"); 403 builder.append(compressToBase64(mDriverStateDump)); 404 } 405 } 406 407 return builder.toString(); 408 } 409 } 410 411 class LimitedCircularArray<E> { 412 private ArrayList<E> mArrayList; 413 private int mMax; LimitedCircularArray(int max)414 LimitedCircularArray(int max) { 415 mArrayList = new ArrayList<E>(max); 416 mMax = max; 417 } 418 addLast(E e)419 public final void addLast(E e) { 420 if (mArrayList.size() >= mMax) 421 mArrayList.remove(0); 422 mArrayList.add(e); 423 } 424 size()425 public final int size() { 426 return mArrayList.size(); 427 } 428 get(int i)429 public final E get(int i) { 430 return mArrayList.get(i); 431 } 432 } 433 434 private final LimitedCircularArray<BugReport> mLastAlerts = 435 new LimitedCircularArray<BugReport>(MAX_ALERT_REPORTS); 436 private final LimitedCircularArray<BugReport> mLastBugReports = 437 new LimitedCircularArray<BugReport>(MAX_BUG_REPORTS); 438 private final HashMap<String, ByteArrayRingBuffer> mRingBufferData = new HashMap(); 439 440 private final WifiNative.WifiLoggerEventHandler mHandler = 441 new WifiNative.WifiLoggerEventHandler() { 442 @Override 443 public void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) { 444 WifiDiagnostics.this.onRingBufferData(status, buffer); 445 } 446 447 @Override 448 public void onWifiAlert(int errorCode, byte[] buffer) { 449 WifiDiagnostics.this.onWifiAlert(errorCode, buffer); 450 } 451 }; 452 onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer)453 synchronized void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) { 454 ByteArrayRingBuffer ring = mRingBufferData.get(status.name); 455 if (ring != null) { 456 ring.appendBuffer(buffer); 457 } 458 } 459 onWifiAlert(int errorCode, @NonNull byte[] buffer)460 synchronized void onWifiAlert(int errorCode, @NonNull byte[] buffer) { 461 captureAlertData(errorCode, buffer); 462 mWifiMetrics.logFirmwareAlert(errorCode); 463 } 464 465 /** 466 * Enables or disables verbose logging 467 * 468 * @param verbose - with the obvious interpretation 469 */ 470 @Override enableVerboseLogging(boolean verboseEnabled)471 public synchronized void enableVerboseLogging(boolean verboseEnabled) { 472 if (verboseEnabled) { 473 mLogLevel = VERBOSE_LOG_WITH_WAKEUP; 474 mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_LARGE; 475 } else { 476 mLogLevel = VERBOSE_NORMAL_LOG; 477 mMaxRingBufferSizeBytes = enableVerboseLoggingForDogfood() 478 ? RING_BUFFER_BYTE_LIMIT_LARGE : RING_BUFFER_BYTE_LIMIT_SMALL; 479 } 480 481 if (!mActiveInterfaces.isEmpty()) { 482 mLog.wC("verbosity changed: restart logging"); 483 startLoggingRingBuffers(); 484 } 485 } 486 isVerboseLoggingEnabled()487 private boolean isVerboseLoggingEnabled() { 488 return mLogLevel > VERBOSE_NORMAL_LOG; 489 } 490 clearVerboseLogs()491 private void clearVerboseLogs() { 492 493 for (int i = 0; i < mLastAlerts.size(); i++) { 494 mLastAlerts.get(i).clearVerboseLogs(); 495 } 496 497 for (int i = 0; i < mLastBugReports.size(); i++) { 498 mLastBugReports.get(i).clearVerboseLogs(); 499 } 500 } 501 fetchRingBuffers()502 private boolean fetchRingBuffers() { 503 if (mRingBuffers != null) return true; 504 505 mRingBuffers = mWifiNative.getRingBufferStatus(); 506 if (mRingBuffers != null) { 507 for (WifiNative.RingBufferStatus buffer : mRingBuffers) { 508 if (DBG) mLog.trace("RingBufferStatus is: %").c(buffer.name).flush(); 509 if (mRingBufferData.containsKey(buffer.name) == false) { 510 mRingBufferData.put(buffer.name, 511 new ByteArrayRingBuffer(mMaxRingBufferSizeBytes)); 512 } 513 if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) { 514 mPerPacketRingBuffer = buffer; 515 } 516 } 517 } else { 518 mLog.wC("no ring buffers found"); 519 } 520 521 return mRingBuffers != null; 522 } 523 resizeRingBuffers()524 private void resizeRingBuffers() { 525 for (ByteArrayRingBuffer byteArrayRingBuffer : mRingBufferData.values()) { 526 byteArrayRingBuffer.resize(mMaxRingBufferSizeBytes); 527 } 528 } 529 startLoggingRingBuffers()530 private void startLoggingRingBuffers() { 531 if (!isVerboseLoggingEnabled()) { 532 clearVerboseLogs(); 533 } 534 if (mRingBuffers == null) { 535 fetchRingBuffers(); 536 } 537 if (mRingBuffers != null) { 538 // Log level may have changed, so restart logging with new levels. 539 stopLoggingAllBuffers(); 540 resizeRingBuffers(); 541 startLoggingAllExceptPerPacketBuffers(); 542 } 543 } 544 startLoggingAllExceptPerPacketBuffers()545 private boolean startLoggingAllExceptPerPacketBuffers() { 546 547 if (mRingBuffers == null) { 548 if (DBG) mLog.tC("No ring buffers to log anything!"); 549 return false; 550 } 551 552 for (WifiNative.RingBufferStatus buffer : mRingBuffers){ 553 554 if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) { 555 /* skip per-packet-buffer */ 556 if (DBG) mLog.trace("skipped per packet logging ring %").c(buffer.name).flush(); 557 continue; 558 } 559 560 startLoggingRingBuffer(buffer); 561 } 562 563 return true; 564 } 565 startLoggingRingBuffer(WifiNative.RingBufferStatus buffer)566 private boolean startLoggingRingBuffer(WifiNative.RingBufferStatus buffer) { 567 568 int minInterval = MinWakeupIntervals[mLogLevel]; 569 int minDataSize = MinBufferSizes[mLogLevel]; 570 571 if (mWifiNative.startLoggingRingBuffer( 572 mLogLevel, 0, minInterval, minDataSize, buffer.name) == false) { 573 if (DBG) mLog.warn("Could not start logging ring %").c(buffer.name).flush(); 574 return false; 575 } 576 577 return true; 578 } 579 stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer)580 private boolean stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer) { 581 if (mWifiNative.startLoggingRingBuffer(0, 0, 0, 0, buffer.name) == false) { 582 if (DBG) mLog.warn("Could not stop logging ring %").c(buffer.name).flush(); 583 } 584 return true; 585 } 586 stopLoggingAllBuffers()587 private boolean stopLoggingAllBuffers() { 588 if (mRingBuffers != null) { 589 for (WifiNative.RingBufferStatus buffer : mRingBuffers) { 590 stopLoggingRingBuffer(buffer); 591 } 592 } 593 return true; 594 } 595 enableVerboseLoggingForDogfood()596 private boolean enableVerboseLoggingForDogfood() { 597 return true; 598 599 } 600 flushDump(int errorCode)601 private boolean flushDump(int errorCode) { 602 if (errorCode == REPORT_REASON_USER_ACTION) return false; 603 604 long currentTime = mClock.getWallClockMillis(); 605 int index = mLastDumpTime.indexOfKey(errorCode); 606 if (index >= 0) { 607 if (currentTime - mLastDumpTime.valueAt(index) < MIN_DUMP_TIME_WINDOW_MILLIS) { 608 return false; 609 } 610 } 611 if (!mWifiNative.flushRingBufferData()) { 612 mLog.wC("could not flush ringbuffer"); 613 return false; 614 } 615 mLastDumpTime.put(errorCode, currentTime); 616 return true; 617 } 618 captureBugreport(int errorCode, boolean captureFWDump)619 private BugReport captureBugreport(int errorCode, boolean captureFWDump) { 620 BugReport report = new BugReport(); 621 report.errorCode = errorCode; 622 report.systemTimeMs = System.currentTimeMillis(); 623 report.kernelTimeNanos = System.nanoTime(); 624 625 if (mRingBuffers != null) { 626 for (WifiNative.RingBufferStatus buffer : mRingBuffers) { 627 /* this will push data in mRingBuffers */ 628 mWifiNative.getRingBufferData(buffer.name); 629 ByteArrayRingBuffer data = mRingBufferData.get(buffer.name); 630 byte[][] buffers = new byte[data.getNumBuffers()][]; 631 for (int i = 0; i < data.getNumBuffers(); i++) { 632 buffers[i] = data.getBuffer(i).clone(); 633 } 634 report.ringBuffers.put(buffer.name, buffers); 635 } 636 } 637 638 report.logcatLines = getLogcat(127); 639 report.kernelLogLines = getKernelLog(127); 640 641 if (captureFWDump) { 642 report.fwMemoryDump = mWifiNative.getFwMemoryDump(); 643 report.mDriverStateDump = mWifiNative.getDriverStateDump(); 644 } 645 return report; 646 } 647 648 @VisibleForTesting getBugReports()649 LimitedCircularArray<BugReport> getBugReports() { 650 return mLastBugReports; 651 } 652 653 @VisibleForTesting getAlertReports()654 LimitedCircularArray<BugReport> getAlertReports() { 655 return mLastAlerts; 656 } 657 compressToBase64(byte[] input)658 private String compressToBase64(byte[] input) { 659 String result; 660 //compress 661 Deflater compressor = new Deflater(); 662 compressor.setLevel(Deflater.BEST_SPEED); 663 compressor.setInput(input); 664 compressor.finish(); 665 ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length); 666 final byte[] buf = new byte[1024]; 667 668 while (!compressor.finished()) { 669 int count = compressor.deflate(buf); 670 bos.write(buf, 0, count); 671 } 672 673 try { 674 compressor.end(); 675 bos.close(); 676 } catch (IOException e) { 677 mLog.wC("ByteArrayOutputStream close error"); 678 result = android.util.Base64.encodeToString(input, Base64.DEFAULT); 679 return result; 680 } 681 682 byte[] compressed = bos.toByteArray(); 683 if (DBG) { 684 mLog.dump("length is: %").c(compressed == null ? 0 : compressed.length).flush(); 685 } 686 687 //encode 688 result = android.util.Base64.encodeToString( 689 compressed.length < input.length ? compressed : input , Base64.DEFAULT); 690 691 if (DBG) { 692 mLog.dump("FwMemoryDump length is: %").c(result.length()).flush(); 693 } 694 695 return result; 696 } 697 getLogcat(int maxLines)698 private ArrayList<String> getLogcat(int maxLines) { 699 ArrayList<String> lines = new ArrayList<String>(maxLines); 700 try { 701 Process process = mJavaRuntime.exec(String.format("logcat -t %d", maxLines)); 702 BufferedReader reader = new BufferedReader( 703 new InputStreamReader(process.getInputStream())); 704 String line; 705 while ((line = reader.readLine()) != null) { 706 lines.add(line); 707 } 708 reader = new BufferedReader( 709 new InputStreamReader(process.getErrorStream())); 710 while ((line = reader.readLine()) != null) { 711 lines.add(line); 712 } 713 process.waitFor(); 714 } catch (InterruptedException|IOException e) { 715 mLog.dump("Exception while capturing logcat: %").c(e.toString()).flush(); 716 } 717 return lines; 718 } 719 getKernelLog(int maxLines)720 private LimitedCircularArray<String> getKernelLog(int maxLines) { 721 if (DBG) mLog.tC("Reading kernel log ..."); 722 LimitedCircularArray<String> lines = new LimitedCircularArray<String>(maxLines); 723 String log = mWifiNative.readKernelLog(); 724 String logLines[] = log.split("\n"); 725 for (int i = 0; i < logLines.length; i++) { 726 lines.addLast(logLines[i]); 727 } 728 if (DBG) mLog.dump("Added % lines").c(logLines.length).flush(); 729 return lines; 730 } 731 732 /** Packet fate reporting */ 733 private ArrayList<WifiNative.FateReport> mPacketFatesForLastFailure; 734 fetchPacketFates()735 private ArrayList<WifiNative.FateReport> fetchPacketFates() { 736 ArrayList<WifiNative.FateReport> mergedFates = new ArrayList<WifiNative.FateReport>(); 737 WifiNative.TxFateReport[] txFates = 738 new WifiNative.TxFateReport[WifiLoggerHal.MAX_FATE_LOG_LEN]; 739 if (mWifiNative.getTxPktFates(mWifiNative.getClientInterfaceName(), txFates)) { 740 for (int i = 0; i < txFates.length && txFates[i] != null; i++) { 741 mergedFates.add(txFates[i]); 742 } 743 } 744 745 WifiNative.RxFateReport[] rxFates = 746 new WifiNative.RxFateReport[WifiLoggerHal.MAX_FATE_LOG_LEN]; 747 if (mWifiNative.getRxPktFates(mWifiNative.getClientInterfaceName(), rxFates)) { 748 for (int i = 0; i < rxFates.length && rxFates[i] != null; i++) { 749 mergedFates.add(rxFates[i]); 750 } 751 } 752 753 Collections.sort(mergedFates, new Comparator<WifiNative.FateReport>() { 754 @Override 755 public int compare(WifiNative.FateReport lhs, WifiNative.FateReport rhs) { 756 return Long.compare(lhs.mDriverTimestampUSec, rhs.mDriverTimestampUSec); 757 } 758 }); 759 760 return mergedFates; 761 } 762 dumpPacketFates(PrintWriter pw)763 private void dumpPacketFates(PrintWriter pw) { 764 dumpPacketFatesInternal(pw, "Last failed connection fates", mPacketFatesForLastFailure, 765 isVerboseLoggingEnabled()); 766 dumpPacketFatesInternal(pw, "Latest fates", fetchPacketFates(), isVerboseLoggingEnabled()); 767 } 768 dumpPacketFatesInternal(PrintWriter pw, String description, ArrayList<WifiNative.FateReport> fates, boolean verbose)769 private static void dumpPacketFatesInternal(PrintWriter pw, String description, 770 ArrayList<WifiNative.FateReport> fates, boolean verbose) { 771 if (fates == null) { 772 pw.format("No fates fetched for \"%s\"\n", description); 773 return; 774 } 775 776 if (fates.size() == 0) { 777 pw.format("HAL provided zero fates for \"%s\"\n", description); 778 return; 779 } 780 781 pw.format("--------------------- %s ----------------------\n", description); 782 783 StringBuilder verboseOutput = new StringBuilder(); 784 pw.print(WifiNative.FateReport.getTableHeader()); 785 for (WifiNative.FateReport fate : fates) { 786 pw.print(fate.toTableRowString()); 787 if (verbose) { 788 // Important: only print Personally Identifiable Information (PII) if verbose 789 // logging is turned on. 790 verboseOutput.append(fate.toVerboseStringWithPiiAllowed()); 791 verboseOutput.append("\n"); 792 } 793 } 794 795 if (verbose) { 796 pw.format("\n>>> VERBOSE PACKET FATE DUMP <<<\n\n"); 797 pw.print(verboseOutput.toString()); 798 } 799 800 pw.println("--------------------------------------------------------------------"); 801 } 802 803 /** 804 * Enable packet fate monitoring. 805 * 806 * @param ifaceName Name of the interface. 807 */ 808 @Override startPktFateMonitoring(@onNull String ifaceName)809 public void startPktFateMonitoring(@NonNull String ifaceName) { 810 if (!mWifiNative.startPktFateMonitoring(ifaceName)) { 811 mLog.wC("Failed to start packet fate monitoring"); 812 } 813 } 814 } 815