Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi;
     18 
     19 import 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 
     91     public WifiLogger(WifiStateMachine wifiStateMachine) {
     92         mWifiStateMachine = wifiStateMachine;
     93     }
     94 
     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 
    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     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 
    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 
    146     public synchronized void captureBugReportData(int reason) {
    147         BugReport report = captureBugreport(reason, true);
    148         mLastBugReports.addLast(report);
    149     }
    150 
    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 
    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 
    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;
    242         LimitedCircularArray(int max) {
    243             mArray = new CircularArray<E>();
    244             mMax = max;
    245         }
    246 
    247         public final void addLast(E e) {
    248             if (mArray.size() >= mMax)
    249                 mArray.popFirst();
    250             mArray.addLast(e);
    251         }
    252 
    253         public final int size() {
    254             return mArray.size();
    255         }
    256 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    359     private boolean stopLoggingAllBuffers() {
    360         if (mRingBuffers != null) {
    361             for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
    362                 stopLoggingRingBuffer(buffer);
    363             }
    364         }
    365         return true;
    366     }
    367 
    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 
    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 
    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