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