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