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