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