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