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