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