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