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.content.Context; 20 import android.util.Base64; 21 22 import com.android.internal.R; 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.server.wifi.util.ByteArrayRingBuffer; 25 import com.android.server.wifi.util.StringUtil; 26 27 import java.io.BufferedReader; 28 import java.io.ByteArrayOutputStream; 29 import java.io.FileDescriptor; 30 import java.io.IOException; 31 import java.io.InputStreamReader; 32 import java.io.PrintWriter; 33 import java.nio.charset.Charset; 34 import java.util.ArrayList; 35 import java.util.Calendar; 36 import java.util.Collections; 37 import java.util.Comparator; 38 import java.util.HashMap; 39 import java.util.zip.Deflater; 40 41 /** 42 * Tracks various logs for framework. 43 */ 44 class WifiDiagnostics extends BaseWifiDiagnostics { 45 /** 46 * Thread-safety: 47 * 1) All non-private methods are |synchronized|. 48 * 2) Callbacks into WifiDiagnostics use non-private (and hence, synchronized) methods. See, e.g, 49 * onRingBufferData(), onWifiAlert(). 50 */ 51 52 private static final String TAG = "WifiDiags"; 53 private static final boolean DBG = false; 54 55 /** log level flags; keep these consistent with wifi_logger.h */ 56 57 /** No logs whatsoever */ 58 public static final int VERBOSE_NO_LOG = 0; 59 /** No logs whatsoever */ 60 public static final int VERBOSE_NORMAL_LOG = 1; 61 /** Be careful since this one can affect performance and power */ 62 public static final int VERBOSE_LOG_WITH_WAKEUP = 2; 63 /** Be careful since this one can affect performance and power and memory */ 64 public static final int VERBOSE_DETAILED_LOG_WITH_WAKEUP = 3; 65 66 /** ring buffer flags; keep these consistent with wifi_logger.h */ 67 public static final int RING_BUFFER_FLAG_HAS_BINARY_ENTRIES = 0x00000001; 68 public static final int RING_BUFFER_FLAG_HAS_ASCII_ENTRIES = 0x00000002; 69 public static final int RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES = 0x00000004; 70 71 /** various reason codes */ 72 public static final int REPORT_REASON_NONE = 0; 73 public static final int REPORT_REASON_ASSOC_FAILURE = 1; 74 public static final int REPORT_REASON_AUTH_FAILURE = 2; 75 public static final int REPORT_REASON_AUTOROAM_FAILURE = 3; 76 public static final int REPORT_REASON_DHCP_FAILURE = 4; 77 public static final int REPORT_REASON_UNEXPECTED_DISCONNECT = 5; 78 public static final int REPORT_REASON_SCAN_FAILURE = 6; 79 public static final int REPORT_REASON_USER_ACTION = 7; 80 81 /** number of bug reports to hold */ 82 public static final int MAX_BUG_REPORTS = 4; 83 84 /** number of alerts to hold */ 85 public static final int MAX_ALERT_REPORTS = 1; 86 87 /** minimum wakeup interval for each of the log levels */ 88 private static final int MinWakeupIntervals[] = new int[] { 0, 3600, 60, 10 }; 89 /** minimum buffer size for each of the log levels */ 90 private static final int MinBufferSizes[] = new int[] { 0, 16384, 16384, 65536 }; 91 92 @VisibleForTesting public static final String FIRMWARE_DUMP_SECTION_HEADER = 93 "FW Memory dump"; 94 @VisibleForTesting public static final String DRIVER_DUMP_SECTION_HEADER = 95 "Driver state dump"; 96 97 private final int RING_BUFFER_BYTE_LIMIT_SMALL; 98 private final int RING_BUFFER_BYTE_LIMIT_LARGE; 99 private int mLogLevel = VERBOSE_NO_LOG; 100 private boolean mIsLoggingEventHandlerRegistered; 101 private WifiNative.RingBufferStatus[] mRingBuffers; 102 private WifiNative.RingBufferStatus mPerPacketRingBuffer; 103 private WifiStateMachine mWifiStateMachine; 104 private final BuildProperties mBuildProperties; 105 private final WifiLog mLog; 106 private final LastMileLogger mLastMileLogger; 107 private final Runtime mJavaRuntime; 108 private int mMaxRingBufferSizeBytes; 109 110 public WifiDiagnostics(Context context, WifiInjector wifiInjector, 111 WifiStateMachine wifiStateMachine, WifiNative wifiNative, 112 BuildProperties buildProperties, LastMileLogger lastMileLogger) { 113 super(wifiNative); 114 RING_BUFFER_BYTE_LIMIT_SMALL = context.getResources().getInteger( 115 R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb) * 1024; 116 RING_BUFFER_BYTE_LIMIT_LARGE = context.getResources().getInteger( 117 R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb) * 1024; 118 119 mWifiStateMachine = wifiStateMachine; 120 mBuildProperties = buildProperties; 121 mIsLoggingEventHandlerRegistered = false; 122 mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL; 123 mLog = wifiInjector.makeLog(TAG); 124 mLastMileLogger = lastMileLogger; 125 mJavaRuntime = wifiInjector.getJavaRuntime(); 126 } 127 128 @Override 129 public synchronized void startLogging(boolean verboseEnabled) { 130 mFirmwareVersion = mWifiNative.getFirmwareVersion(); 131 mDriverVersion = mWifiNative.getDriverVersion(); 132 mSupportedFeatureSet = mWifiNative.getSupportedLoggerFeatureSet(); 133 134 if (!mIsLoggingEventHandlerRegistered) { 135 mIsLoggingEventHandlerRegistered = mWifiNative.setLoggingEventHandler(mHandler); 136 } 137 138 if (verboseEnabled) { 139 mLogLevel = VERBOSE_LOG_WITH_WAKEUP; 140 mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_LARGE; 141 } else { 142 mLogLevel = VERBOSE_NORMAL_LOG; 143 mMaxRingBufferSizeBytes = enableVerboseLoggingForDogfood() 144 ? RING_BUFFER_BYTE_LIMIT_LARGE : RING_BUFFER_BYTE_LIMIT_SMALL; 145 clearVerboseLogs(); 146 } 147 148 if (mRingBuffers == null) { 149 fetchRingBuffers(); 150 } 151 152 if (mRingBuffers != null) { 153 /* log level may have changed, so restart logging with new levels */ 154 stopLoggingAllBuffers(); 155 resizeRingBuffers(); 156 startLoggingAllExceptPerPacketBuffers(); 157 } 158 159 if (!mWifiNative.startPktFateMonitoring()) { 160 mLog.wC("Failed to start packet fate monitoring"); 161 } 162 } 163 164 @Override 165 public synchronized void startPacketLog() { 166 if (mPerPacketRingBuffer != null) { 167 startLoggingRingBuffer(mPerPacketRingBuffer); 168 } else { 169 if (DBG) mLog.tC("There is no per packet ring buffer"); 170 } 171 } 172 173 @Override 174 public synchronized void stopPacketLog() { 175 if (mPerPacketRingBuffer != null) { 176 stopLoggingRingBuffer(mPerPacketRingBuffer); 177 } else { 178 if (DBG) mLog.tC("There is no per packet ring buffer"); 179 } 180 } 181 182 @Override 183 public synchronized void stopLogging() { 184 if (mIsLoggingEventHandlerRegistered) { 185 if (!mWifiNative.resetLogHandler()) { 186 mLog.wC("Fail to reset log handler"); 187 } else { 188 if (DBG) mLog.tC("Reset log handler"); 189 } 190 // Clear mIsLoggingEventHandlerRegistered even if resetLogHandler() failed, because 191 // the log handler is in an indeterminate state. 192 mIsLoggingEventHandlerRegistered = false; 193 } 194 if (mLogLevel != VERBOSE_NO_LOG) { 195 stopLoggingAllBuffers(); 196 mRingBuffers = null; 197 mLogLevel = VERBOSE_NO_LOG; 198 } 199 } 200 201 @Override 202 synchronized void reportConnectionEvent(long connectionId, byte event) { 203 mLastMileLogger.reportConnectionEvent(connectionId, event); 204 if (event == CONNECTION_EVENT_FAILED) { 205 mPacketFatesForLastFailure = fetchPacketFates(); 206 } 207 } 208 209 @Override 210 public synchronized void captureBugReportData(int reason) { 211 BugReport report = captureBugreport(reason, isVerboseLoggingEnabled()); 212 mLastBugReports.addLast(report); 213 } 214 215 @Override 216 public synchronized void captureAlertData(int errorCode, byte[] alertData) { 217 BugReport report = captureBugreport(errorCode, isVerboseLoggingEnabled()); 218 report.alertData = alertData; 219 mLastAlerts.addLast(report); 220 } 221 222 @Override 223 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 224 super.dump(pw); 225 226 for (int i = 0; i < mLastAlerts.size(); i++) { 227 pw.println("--------------------------------------------------------------------"); 228 pw.println("Alert dump " + i); 229 pw.print(mLastAlerts.get(i)); 230 pw.println("--------------------------------------------------------------------"); 231 } 232 233 for (int i = 0; i < mLastBugReports.size(); i++) { 234 pw.println("--------------------------------------------------------------------"); 235 pw.println("Bug dump " + i); 236 pw.print(mLastBugReports.get(i)); 237 pw.println("--------------------------------------------------------------------"); 238 } 239 240 dumpPacketFates(pw); 241 mLastMileLogger.dump(pw); 242 243 pw.println("--------------------------------------------------------------------"); 244 } 245 246 /* private methods and data */ 247 class BugReport { 248 long systemTimeMs; 249 long kernelTimeNanos; 250 int errorCode; 251 HashMap<String, byte[][]> ringBuffers = new HashMap(); 252 byte[] fwMemoryDump; 253 byte[] mDriverStateDump; 254 byte[] alertData; 255 LimitedCircularArray<String> kernelLogLines; 256 ArrayList<String> logcatLines; 257 258 void clearVerboseLogs() { 259 fwMemoryDump = null; 260 mDriverStateDump = null; 261 } 262 263 public String toString() { 264 StringBuilder builder = new StringBuilder(); 265 266 Calendar c = Calendar.getInstance(); 267 c.setTimeInMillis(systemTimeMs); 268 builder.append("system time = ").append( 269 String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)).append("\n"); 270 271 long kernelTimeMs = kernelTimeNanos/(1000*1000); 272 builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append 273 (kernelTimeMs%1000).append("\n"); 274 275 if (alertData == null) 276 builder.append("reason = ").append(errorCode).append("\n"); 277 else { 278 builder.append("errorCode = ").append(errorCode); 279 builder.append("data \n"); 280 builder.append(compressToBase64(alertData)).append("\n"); 281 } 282 283 if (kernelLogLines != null) { 284 builder.append("kernel log: \n"); 285 for (int i = 0; i < kernelLogLines.size(); i++) { 286 builder.append(kernelLogLines.get(i)).append("\n"); 287 } 288 builder.append("\n"); 289 } 290 291 if (logcatLines != null) { 292 builder.append("system log: \n"); 293 for (int i = 0; i < logcatLines.size(); i++) { 294 builder.append(logcatLines.get(i)).append("\n"); 295 } 296 builder.append("\n"); 297 } 298 299 for (HashMap.Entry<String, byte[][]> e : ringBuffers.entrySet()) { 300 String ringName = e.getKey(); 301 byte[][] buffers = e.getValue(); 302 builder.append("ring-buffer = ").append(ringName).append("\n"); 303 304 int size = 0; 305 for (int i = 0; i < buffers.length; i++) { 306 size += buffers[i].length; 307 } 308 309 byte[] buffer = new byte[size]; 310 int index = 0; 311 for (int i = 0; i < buffers.length; i++) { 312 System.arraycopy(buffers[i], 0, buffer, index, buffers[i].length); 313 index += buffers[i].length; 314 } 315 316 builder.append(compressToBase64(buffer)); 317 builder.append("\n"); 318 } 319 320 if (fwMemoryDump != null) { 321 builder.append(FIRMWARE_DUMP_SECTION_HEADER); 322 builder.append("\n"); 323 builder.append(compressToBase64(fwMemoryDump)); 324 builder.append("\n"); 325 } 326 327 if (mDriverStateDump != null) { 328 builder.append(DRIVER_DUMP_SECTION_HEADER); 329 if (StringUtil.isAsciiPrintable(mDriverStateDump)) { 330 builder.append(" (ascii)\n"); 331 builder.append(new String(mDriverStateDump, Charset.forName("US-ASCII"))); 332 builder.append("\n"); 333 } else { 334 builder.append(" (base64)\n"); 335 builder.append(compressToBase64(mDriverStateDump)); 336 } 337 } 338 339 return builder.toString(); 340 } 341 } 342 343 class LimitedCircularArray<E> { 344 private ArrayList<E> mArrayList; 345 private int mMax; 346 LimitedCircularArray(int max) { 347 mArrayList = new ArrayList<E>(max); 348 mMax = max; 349 } 350 351 public final void addLast(E e) { 352 if (mArrayList.size() >= mMax) 353 mArrayList.remove(0); 354 mArrayList.add(e); 355 } 356 357 public final int size() { 358 return mArrayList.size(); 359 } 360 361 public final E get(int i) { 362 return mArrayList.get(i); 363 } 364 } 365 366 private final LimitedCircularArray<BugReport> mLastAlerts = 367 new LimitedCircularArray<BugReport>(MAX_ALERT_REPORTS); 368 private final LimitedCircularArray<BugReport> mLastBugReports = 369 new LimitedCircularArray<BugReport>(MAX_BUG_REPORTS); 370 private final HashMap<String, ByteArrayRingBuffer> mRingBufferData = new HashMap(); 371 372 private final WifiNative.WifiLoggerEventHandler mHandler = 373 new WifiNative.WifiLoggerEventHandler() { 374 @Override 375 public void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) { 376 WifiDiagnostics.this.onRingBufferData(status, buffer); 377 } 378 379 @Override 380 public void onWifiAlert(int errorCode, byte[] buffer) { 381 WifiDiagnostics.this.onWifiAlert(errorCode, buffer); 382 } 383 }; 384 385 synchronized void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) { 386 ByteArrayRingBuffer ring = mRingBufferData.get(status.name); 387 if (ring != null) { 388 ring.appendBuffer(buffer); 389 } 390 } 391 392 synchronized void onWifiAlert(int errorCode, byte[] buffer) { 393 if (mWifiStateMachine != null) { 394 mWifiStateMachine.sendMessage( 395 WifiStateMachine.CMD_FIRMWARE_ALERT, errorCode, 0, buffer); 396 } 397 } 398 399 private boolean isVerboseLoggingEnabled() { 400 return mLogLevel > VERBOSE_NORMAL_LOG; 401 } 402 403 private void clearVerboseLogs() { 404 mPacketFatesForLastFailure = null; 405 406 for (int i = 0; i < mLastAlerts.size(); i++) { 407 mLastAlerts.get(i).clearVerboseLogs(); 408 } 409 410 for (int i = 0; i < mLastBugReports.size(); i++) { 411 mLastBugReports.get(i).clearVerboseLogs(); 412 } 413 } 414 415 private boolean fetchRingBuffers() { 416 if (mBuildProperties.isUserBuild()) { 417 mRingBuffers = null; 418 return false; 419 } 420 421 if (mRingBuffers != null) return true; 422 423 mRingBuffers = mWifiNative.getRingBufferStatus(); 424 if (mRingBuffers != null) { 425 for (WifiNative.RingBufferStatus buffer : mRingBuffers) { 426 if (DBG) mLog.trace("RingBufferStatus is: %").c(buffer.name).flush(); 427 if (mRingBufferData.containsKey(buffer.name) == false) { 428 mRingBufferData.put(buffer.name, 429 new ByteArrayRingBuffer(mMaxRingBufferSizeBytes)); 430 } 431 if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) { 432 mPerPacketRingBuffer = buffer; 433 } 434 } 435 } else { 436 mLog.wC("no ring buffers found"); 437 } 438 439 return mRingBuffers != null; 440 } 441 442 private void resizeRingBuffers() { 443 for (ByteArrayRingBuffer byteArrayRingBuffer : mRingBufferData.values()) { 444 byteArrayRingBuffer.resize(mMaxRingBufferSizeBytes); 445 } 446 } 447 448 private boolean startLoggingAllExceptPerPacketBuffers() { 449 450 if (mRingBuffers == null) { 451 if (DBG) mLog.tC("No ring buffers to log anything!"); 452 return false; 453 } 454 455 for (WifiNative.RingBufferStatus buffer : mRingBuffers){ 456 457 if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) { 458 /* skip per-packet-buffer */ 459 if (DBG) mLog.trace("skipped per packet logging ring %").c(buffer.name).flush(); 460 continue; 461 } 462 463 startLoggingRingBuffer(buffer); 464 } 465 466 return true; 467 } 468 469 private boolean startLoggingRingBuffer(WifiNative.RingBufferStatus buffer) { 470 471 int minInterval = MinWakeupIntervals[mLogLevel]; 472 int minDataSize = MinBufferSizes[mLogLevel]; 473 474 if (mWifiNative.startLoggingRingBuffer( 475 mLogLevel, 0, minInterval, minDataSize, buffer.name) == false) { 476 if (DBG) mLog.warn("Could not start logging ring %").c(buffer.name).flush(); 477 return false; 478 } 479 480 return true; 481 } 482 483 private boolean stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer) { 484 if (mWifiNative.startLoggingRingBuffer(0, 0, 0, 0, buffer.name) == false) { 485 if (DBG) mLog.warn("Could not stop logging ring %").c(buffer.name).flush(); 486 } 487 return true; 488 } 489 490 private boolean stopLoggingAllBuffers() { 491 if (mRingBuffers != null) { 492 for (WifiNative.RingBufferStatus buffer : mRingBuffers) { 493 stopLoggingRingBuffer(buffer); 494 } 495 } 496 return true; 497 } 498 499 private boolean enableVerboseLoggingForDogfood() { 500 return false; 501 } 502 503 private BugReport captureBugreport(int errorCode, boolean captureFWDump) { 504 BugReport report = new BugReport(); 505 report.errorCode = errorCode; 506 report.systemTimeMs = System.currentTimeMillis(); 507 report.kernelTimeNanos = System.nanoTime(); 508 509 if (mRingBuffers != null) { 510 for (WifiNative.RingBufferStatus buffer : mRingBuffers) { 511 /* this will push data in mRingBuffers */ 512 mWifiNative.getRingBufferData(buffer.name); 513 ByteArrayRingBuffer data = mRingBufferData.get(buffer.name); 514 byte[][] buffers = new byte[data.getNumBuffers()][]; 515 for (int i = 0; i < data.getNumBuffers(); i++) { 516 buffers[i] = data.getBuffer(i).clone(); 517 } 518 report.ringBuffers.put(buffer.name, buffers); 519 } 520 } 521 522 report.logcatLines = getLogcat(127); 523 report.kernelLogLines = getKernelLog(127); 524 525 if (captureFWDump) { 526 report.fwMemoryDump = mWifiNative.getFwMemoryDump(); 527 report.mDriverStateDump = mWifiNative.getDriverStateDump(); 528 } 529 return report; 530 } 531 532 @VisibleForTesting 533 LimitedCircularArray<BugReport> getBugReports() { 534 return mLastBugReports; 535 } 536 537 private String compressToBase64(byte[] input) { 538 String result; 539 //compress 540 Deflater compressor = new Deflater(); 541 compressor.setLevel(Deflater.BEST_SPEED); 542 compressor.setInput(input); 543 compressor.finish(); 544 ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length); 545 final byte[] buf = new byte[1024]; 546 547 while (!compressor.finished()) { 548 int count = compressor.deflate(buf); 549 bos.write(buf, 0, count); 550 } 551 552 try { 553 compressor.end(); 554 bos.close(); 555 } catch (IOException e) { 556 mLog.wC("ByteArrayOutputStream close error"); 557 result = android.util.Base64.encodeToString(input, Base64.DEFAULT); 558 return result; 559 } 560 561 byte[] compressed = bos.toByteArray(); 562 if (DBG) { 563 mLog.dump("length is: %").c(compressed == null ? 0 : compressed.length).flush(); 564 } 565 566 //encode 567 result = android.util.Base64.encodeToString( 568 compressed.length < input.length ? compressed : input , Base64.DEFAULT); 569 570 if (DBG) { 571 mLog.dump("FwMemoryDump length is: %").c(result.length()).flush(); 572 } 573 574 return result; 575 } 576 577 private ArrayList<String> getLogcat(int maxLines) { 578 ArrayList<String> lines = new ArrayList<String>(maxLines); 579 try { 580 Process process = mJavaRuntime.exec(String.format("logcat -t %d", maxLines)); 581 BufferedReader reader = new BufferedReader( 582 new InputStreamReader(process.getInputStream())); 583 String line; 584 while ((line = reader.readLine()) != null) { 585 lines.add(line); 586 } 587 reader = new BufferedReader( 588 new InputStreamReader(process.getErrorStream())); 589 while ((line = reader.readLine()) != null) { 590 lines.add(line); 591 } 592 process.waitFor(); 593 } catch (InterruptedException|IOException e) { 594 mLog.dump("Exception while capturing logcat: %").c(e.toString()).flush(); 595 } 596 return lines; 597 } 598 599 private LimitedCircularArray<String> getKernelLog(int maxLines) { 600 if (DBG) mLog.tC("Reading kernel log ..."); 601 LimitedCircularArray<String> lines = new LimitedCircularArray<String>(maxLines); 602 String log = mWifiNative.readKernelLog(); 603 String logLines[] = log.split("\n"); 604 for (int i = 0; i < logLines.length; i++) { 605 lines.addLast(logLines[i]); 606 } 607 if (DBG) mLog.dump("Added % lines").c(logLines.length).flush(); 608 return lines; 609 } 610 611 /** Packet fate reporting */ 612 private ArrayList<WifiNative.FateReport> mPacketFatesForLastFailure; 613 614 private ArrayList<WifiNative.FateReport> fetchPacketFates() { 615 ArrayList<WifiNative.FateReport> mergedFates = new ArrayList<WifiNative.FateReport>(); 616 WifiNative.TxFateReport[] txFates = 617 new WifiNative.TxFateReport[WifiLoggerHal.MAX_FATE_LOG_LEN]; 618 if (mWifiNative.getTxPktFates(txFates)) { 619 for (int i = 0; i < txFates.length && txFates[i] != null; i++) { 620 mergedFates.add(txFates[i]); 621 } 622 } 623 624 WifiNative.RxFateReport[] rxFates = 625 new WifiNative.RxFateReport[WifiLoggerHal.MAX_FATE_LOG_LEN]; 626 if (mWifiNative.getRxPktFates(rxFates)) { 627 for (int i = 0; i < rxFates.length && rxFates[i] != null; i++) { 628 mergedFates.add(rxFates[i]); 629 } 630 } 631 632 Collections.sort(mergedFates, new Comparator<WifiNative.FateReport>() { 633 @Override 634 public int compare(WifiNative.FateReport lhs, WifiNative.FateReport rhs) { 635 return Long.compare(lhs.mDriverTimestampUSec, rhs.mDriverTimestampUSec); 636 } 637 }); 638 639 return mergedFates; 640 } 641 642 private void dumpPacketFates(PrintWriter pw) { 643 dumpPacketFatesInternal(pw, "Last failed connection fates", mPacketFatesForLastFailure, 644 isVerboseLoggingEnabled()); 645 dumpPacketFatesInternal(pw, "Latest fates", fetchPacketFates(), isVerboseLoggingEnabled()); 646 } 647 648 private static void dumpPacketFatesInternal(PrintWriter pw, String description, 649 ArrayList<WifiNative.FateReport> fates, boolean verbose) { 650 if (fates == null) { 651 pw.format("No fates fetched for \"%s\"\n", description); 652 return; 653 } 654 655 if (fates.size() == 0) { 656 pw.format("HAL provided zero fates for \"%s\"\n", description); 657 return; 658 } 659 660 pw.format("--------------------- %s ----------------------\n", description); 661 662 StringBuilder verboseOutput = new StringBuilder(); 663 pw.print(WifiNative.FateReport.getTableHeader()); 664 for (WifiNative.FateReport fate : fates) { 665 pw.print(fate.toTableRowString()); 666 if (verbose) { 667 // Important: only print Personally Identifiable Information (PII) if verbose 668 // logging is turned on. 669 verboseOutput.append(fate.toVerboseStringWithPiiAllowed()); 670 verboseOutput.append("\n"); 671 } 672 } 673 674 if (verbose) { 675 pw.format("\n>>> VERBOSE PACKET FATE DUMP <<<\n\n"); 676 pw.print(verboseOutput.toString()); 677 } 678 679 pw.println("--------------------------------------------------------------------"); 680 } 681 } 682