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