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 com.android.internal.app.IBatteryStats;
20 import com.android.internal.util.Protocol;
21 
22 import android.support.v4.util.CircularArray;
23 import android.util.Base64;
24 import android.util.LocalLog;
25 import android.util.Log;
26 
27 import java.io.ByteArrayOutputStream;
28 import java.io.FileDescriptor;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.util.Calendar;
32 import java.util.HashMap;
33 import java.util.zip.Deflater;
34 
35 /**
36  * Tracks various logs for framework
37  */
38 class WifiLogger  {
39 
40     private static final String TAG = "WifiLogger";
41     private static final boolean DBG = false;
42 
43     /** log level flags; keep these consistent with wifi_logger.h */
44 
45     /** No logs whatsoever */
46     public static final int VERBOSE_NO_LOG = 0;
47     /** No logs whatsoever */
48     public static final int VERBOSE_NORMAL_LOG = 1;
49     /** Be careful since this one can affect performance and power */
50     public static final int VERBOSE_LOG_WITH_WAKEUP  = 2;
51     /** Be careful since this one can affect performance and power and memory */
52     public static final int VERBOSE_DETAILED_LOG_WITH_WAKEUP  = 3;
53 
54     /** ring buffer flags; keep these consistent with wifi_logger.h */
55     public static final int RING_BUFFER_FLAG_HAS_BINARY_ENTRIES     = 0x00000001;
56     public static final int RING_BUFFER_FLAG_HAS_ASCII_ENTRIES      = 0x00000002;
57     public static final int RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES = 0x00000004;
58 
59     /** various reason codes */
60     public static final int REPORT_REASON_NONE                      = 0;
61     public static final int REPORT_REASON_ASSOC_FAILURE             = 1;
62     public static final int REPORT_REASON_AUTH_FAILURE              = 2;
63     public static final int REPORT_REASON_AUTOROAM_FAILURE          = 3;
64     public static final int REPORT_REASON_DHCP_FAILURE              = 4;
65     public static final int REPORT_REASON_UNEXPECTED_DISCONNECT     = 5;
66     public static final int REPORT_REASON_SCAN_FAILURE              = 6;
67     public static final int REPORT_REASON_USER_ACTION               = 7;
68 
69     /** number of ring buffer entries to cache */
70     public static final int MAX_RING_BUFFERS                        = 10;
71 
72     /** number of bug reports to hold */
73     public static final int MAX_BUG_REPORTS                         = 4;
74 
75     /** number of alerts to hold */
76     public static final int MAX_ALERT_REPORTS                       = 1;
77 
78     /** minimum wakeup interval for each of the log levels */
79     private static final int MinWakeupIntervals[] = new int[] { 0, 3600, 60, 10 };
80     /** minimum buffer size for each of the log levels */
81     private static final int MinBufferSizes[] = new int[] { 0, 16384, 16384, 65536 };
82 
83     private int mLogLevel = VERBOSE_NO_LOG;
84     private String mFirmwareVersion;
85     private String mDriverVersion;
86     private int mSupportedFeatureSet;
87     private WifiNative.RingBufferStatus[] mRingBuffers;
88     private WifiNative.RingBufferStatus mPerPacketRingBuffer;
89     private WifiStateMachine mWifiStateMachine;
90 
WifiLogger(WifiStateMachine wifiStateMachine)91     public WifiLogger(WifiStateMachine wifiStateMachine) {
92         mWifiStateMachine = wifiStateMachine;
93     }
94 
startLogging(boolean verboseEnabled)95     public synchronized void startLogging(boolean verboseEnabled) {
96         mFirmwareVersion = WifiNative.getFirmwareVersion();
97         mDriverVersion = WifiNative.getDriverVersion();
98         mSupportedFeatureSet = WifiNative.getSupportedLoggerFeatureSet();
99 
100         if (mLogLevel == VERBOSE_NO_LOG)
101             WifiNative.setLoggingEventHandler(mHandler);
102 
103         if (verboseEnabled) {
104             mLogLevel = VERBOSE_LOG_WITH_WAKEUP;
105         } else {
106             mLogLevel = VERBOSE_NORMAL_LOG;
107         }
108         if (mRingBuffers == null) {
109             if (fetchRingBuffers()) {
110                 startLoggingAllExceptPerPacketBuffers();
111             }
112         }
113     }
114 
startPacketLog()115     public synchronized void startPacketLog() {
116         if (mPerPacketRingBuffer != null) {
117             startLoggingRingBuffer(mPerPacketRingBuffer);
118         } else {
119             if (DBG) Log.d(TAG, "There is no per packet ring buffer");
120         }
121     }
122 
stopPacketLog()123     public synchronized void stopPacketLog() {
124         if (mPerPacketRingBuffer != null) {
125             stopLoggingRingBuffer(mPerPacketRingBuffer);
126         } else {
127             if (DBG) Log.d(TAG, "There is no per packet ring buffer");
128         }
129     }
130 
stopLogging()131     public synchronized void stopLogging() {
132         if (mLogLevel != VERBOSE_NO_LOG) {
133             //resetLogHandler only can be used when you terminate all logging since all handler will
134             //be removed. This also stop alert logging
135             if(!WifiNative.resetLogHandler()) {
136                 Log.e(TAG, "Fail to reset log handler");
137             } else {
138                 if (DBG) Log.d(TAG,"Reset log handler");
139             }
140             stopLoggingAllBuffers();
141             mRingBuffers = null;
142             mLogLevel = VERBOSE_NO_LOG;
143         }
144     }
145 
captureBugReportData(int reason)146     public synchronized void captureBugReportData(int reason) {
147         BugReport report = captureBugreport(reason, true);
148         mLastBugReports.addLast(report);
149     }
150 
captureAlertData(int errorCode, byte[] alertData)151     public synchronized void captureAlertData(int errorCode, byte[] alertData) {
152         BugReport report = captureBugreport(errorCode, /* captureFWDump = */ true);
153         report.alertData = alertData;
154         mLastAlerts.addLast(report);
155     }
156 
dump(FileDescriptor fd, PrintWriter pw, String[] args)157     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
158         pw.println("Chipset information :-----------------------------------------------");
159         pw.println("FW Version is: " + mFirmwareVersion);
160         pw.println("Driver Version is: " + mDriverVersion);
161         pw.println("Supported Feature set: " + mSupportedFeatureSet);
162 
163         for (int i = 0; i < mLastAlerts.size(); i++) {
164             pw.println("--------------------------------------------------------------------");
165             pw.println("Alert dump " + i);
166             pw.print(mLastAlerts.get(i));
167             pw.println("--------------------------------------------------------------------");
168         }
169 
170         for (int i = 0; i < mLastBugReports.size(); i++) {
171             pw.println("--------------------------------------------------------------------");
172             pw.println("Bug dump " + i);
173             pw.print(mLastBugReports.get(i));
174             pw.println("--------------------------------------------------------------------");
175         }
176 
177         pw.println("--------------------------------------------------------------------");
178     }
179 
180     /* private methods and data */
181     private static class BugReport {
182         long systemTimeMs;
183         long kernelTimeNanos;
184         int errorCode;
185         HashMap<String, byte[][]> ringBuffers = new HashMap();
186         byte[] fwMemoryDump;
187         byte[] alertData;
188 
toString()189         public String toString() {
190             StringBuilder builder = new StringBuilder();
191 
192             Calendar c = Calendar.getInstance();
193             c.setTimeInMillis(systemTimeMs);
194             builder.append("system time = ").append(
195                     String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)).append("\n");
196 
197             long kernelTimeMs = kernelTimeNanos/(1000*1000);
198             builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
199                     (kernelTimeMs%1000).append("\n");
200 
201             if (alertData == null)
202                 builder.append("reason = ").append(errorCode).append("\n");
203             else {
204                 builder.append("errorCode = ").append(errorCode);
205                 builder.append("data \n");
206                 builder.append(compressToBase64(alertData)).append("\n");
207             }
208 
209             for (HashMap.Entry<String, byte[][]> e : ringBuffers.entrySet()) {
210                 String ringName = e.getKey();
211                 byte[][] buffers = e.getValue();
212                 builder.append("ring-buffer = ").append(ringName).append("\n");
213 
214                 int size = 0;
215                 for (int i = 0; i < buffers.length; i++) {
216                     size += buffers[i].length;
217                 }
218 
219                 byte[] buffer = new byte[size];
220                 int index = 0;
221                 for (int i = 0; i < buffers.length; i++) {
222                     System.arraycopy(buffers[i], 0, buffer, index, buffers[i].length);
223                     index += buffers[i].length;
224                 }
225 
226                 builder.append(compressToBase64(buffer));
227                 builder.append("\n");
228             }
229 
230             if (fwMemoryDump != null) {
231                 builder.append("FW Memory dump \n");
232                 builder.append(compressToBase64(fwMemoryDump));
233             }
234 
235             return builder.toString();
236         }
237     }
238 
239     static class LimitedCircularArray<E> {
240         private CircularArray<E> mArray;
241         private int mMax;
LimitedCircularArray(int max)242         LimitedCircularArray(int max) {
243             mArray = new CircularArray<E>();
244             mMax = max;
245         }
246 
addLast(E e)247         public final void addLast(E e) {
248             if (mArray.size() >= mMax)
249                 mArray.popFirst();
250             mArray.addLast(e);
251         }
252 
size()253         public final int size() {
254             return mArray.size();
255         }
256 
get(int i)257         public final E get(int i) {
258             return mArray.get(i);
259         }
260     }
261 
262     private final LimitedCircularArray<BugReport> mLastAlerts =
263             new LimitedCircularArray<BugReport>(MAX_ALERT_REPORTS);
264     private final LimitedCircularArray<BugReport> mLastBugReports =
265             new LimitedCircularArray<BugReport>(MAX_BUG_REPORTS);
266     private final HashMap<String, LimitedCircularArray<byte[]>> mRingBufferData = new HashMap();
267 
268     private final WifiNative.WifiLoggerEventHandler mHandler =
269             new WifiNative.WifiLoggerEventHandler() {
270         @Override
271         public void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
272             WifiLogger.this.onRingBufferData(status, buffer);
273         }
274 
275         @Override
276         public void onWifiAlert(int errorCode, byte[] buffer) {
277             WifiLogger.this.onWifiAlert(errorCode, buffer);
278         }
279     };
280 
onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer)281     synchronized void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
282         LimitedCircularArray<byte[]> ring = mRingBufferData.get(status.name);
283         if (ring != null) {
284             ring.addLast(buffer);
285         }
286     }
287 
onWifiAlert(int errorCode, byte[] buffer)288     synchronized void onWifiAlert(int errorCode, byte[] buffer) {
289         if (mWifiStateMachine != null) {
290             mWifiStateMachine.sendMessage(
291                     WifiStateMachine.CMD_FIRMWARE_ALERT, errorCode, 0, buffer);
292         }
293     }
294 
fetchRingBuffers()295     private boolean fetchRingBuffers() {
296         if (mRingBuffers != null) return true;
297 
298         mRingBuffers = WifiNative.getRingBufferStatus();
299         if (mRingBuffers != null) {
300             for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
301                 if (DBG) Log.d(TAG, "RingBufferStatus is: \n" + buffer.name);
302                 if (mRingBufferData.containsKey(buffer.name) == false) {
303                     mRingBufferData.put(buffer.name,
304                             new LimitedCircularArray<byte[]>(MAX_RING_BUFFERS));
305                 }
306                 if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
307                     mPerPacketRingBuffer = buffer;
308                 }
309             }
310         } else {
311             Log.e(TAG, "no ring buffers found");
312         }
313 
314         return mRingBuffers != null;
315     }
316 
startLoggingAllExceptPerPacketBuffers()317     private boolean startLoggingAllExceptPerPacketBuffers() {
318 
319         if (mRingBuffers == null) {
320             if (DBG) Log.d(TAG, "No ring buffers to log anything!");
321             return false;
322         }
323 
324         for (WifiNative.RingBufferStatus buffer : mRingBuffers){
325 
326             if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
327                 /* skip per-packet-buffer */
328                 if (DBG) Log.d(TAG, "skipped per packet logging ring " + buffer.name);
329                 continue;
330             }
331 
332             startLoggingRingBuffer(buffer);
333         }
334 
335         return true;
336     }
337 
startLoggingRingBuffer(WifiNative.RingBufferStatus buffer)338     private boolean startLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
339 
340         int minInterval = MinWakeupIntervals[mLogLevel];
341         int minDataSize = MinBufferSizes[mLogLevel];
342 
343         if (WifiNative.startLoggingRingBuffer(
344                 mLogLevel, 0, minInterval, minDataSize, buffer.name) == false) {
345             if (DBG) Log.e(TAG, "Could not start logging ring " + buffer.name);
346             return false;
347         }
348 
349         return true;
350     }
351 
stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer)352     private boolean stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
353         if (WifiNative.startLoggingRingBuffer(0, 0, 0, 0, buffer.name) == false) {
354             if (DBG) Log.e(TAG, "Could not stop logging ring " + buffer.name);
355         }
356         return true;
357     }
358 
stopLoggingAllBuffers()359     private boolean stopLoggingAllBuffers() {
360         if (mRingBuffers != null) {
361             for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
362                 stopLoggingRingBuffer(buffer);
363             }
364         }
365         return true;
366     }
367 
getAllRingBufferData()368     private boolean getAllRingBufferData() {
369         if (mRingBuffers == null) {
370             Log.e(TAG, "Not ring buffers available to collect data!");
371             return false;
372         }
373 
374         for (WifiNative.RingBufferStatus element : mRingBuffers){
375             boolean result = WifiNative.getRingBufferData(element.name);
376             if (!result) {
377                 Log.e(TAG, "Fail to get ring buffer data of: " + element.name);
378                 return false;
379             }
380         }
381 
382         Log.d(TAG, "getAllRingBufferData Successfully!");
383         return true;
384     }
385 
captureBugreport(int errorCode, boolean captureFWDump)386     private BugReport captureBugreport(int errorCode, boolean captureFWDump) {
387         BugReport report = new BugReport();
388         report.errorCode = errorCode;
389         report.systemTimeMs = System.currentTimeMillis();
390         report.kernelTimeNanos = System.nanoTime();
391 
392         if (mRingBuffers != null) {
393             for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
394                 /* this will push data in mRingBuffers */
395                 WifiNative.getRingBufferData(buffer.name);
396                 LimitedCircularArray<byte[]> data = mRingBufferData.get(buffer.name);
397                 byte[][] buffers = new byte[data.size()][];
398                 for (int i = 0; i < data.size(); i++) {
399                     buffers[i] = data.get(i).clone();
400                 }
401                 report.ringBuffers.put(buffer.name, buffers);
402             }
403         }
404 
405         if (captureFWDump) {
406             report.fwMemoryDump = WifiNative.getFwMemoryDump();
407         }
408         return report;
409     }
410 
compressToBase64(byte[] input)411     private static String compressToBase64(byte[] input) {
412         String result;
413         //compress
414         Deflater compressor = new Deflater();
415         compressor.setLevel(Deflater.BEST_COMPRESSION);
416         compressor.setInput(input);
417         compressor.finish();
418         ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
419         final byte[] buf = new byte[1024];
420 
421         while (!compressor.finished()) {
422             int count = compressor.deflate(buf);
423             bos.write(buf, 0, count);
424         }
425 
426         try {
427             compressor.end();
428             bos.close();
429         } catch (IOException e) {
430             Log.e(TAG, "ByteArrayOutputStream close error");
431             result =  android.util.Base64.encodeToString(input, Base64.DEFAULT);
432             return result;
433         }
434 
435         byte[] compressed = bos.toByteArray();
436         if (DBG) {
437             Log.d(TAG," length is:" + (compressed == null? "0" : compressed.length));
438         }
439 
440         //encode
441         result = android.util.Base64.encodeToString(
442                 compressed.length < input.length ? compressed : input , Base64.DEFAULT);
443 
444         if (DBG) {
445             Log.d(TAG, "FwMemoryDump length is :" + result.length());
446         }
447 
448         return result;
449     }
450 }
451