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