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