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