259 lines
7.4 KiB
Java
259 lines
7.4 KiB
Java
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server.wifi;
|
|
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.annotations.Immutable;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
|
|
import javax.annotation.concurrent.ThreadSafe;
|
|
|
|
/**
|
|
* Provides a WifiLog implementation which uses logd as the
|
|
* logging backend.
|
|
*
|
|
* This class is trivially thread-safe, as instances are immutable.
|
|
* Note, however, that LogMessage instances are _not_ thread-safe.
|
|
*/
|
|
@ThreadSafe
|
|
@Immutable
|
|
class LogcatLog implements WifiLog {
|
|
private final String mTag;
|
|
private static volatile boolean sVerboseLogging = false;
|
|
private static final NoLogMessage NO_LOG_MESSAGE = new NoLogMessage();
|
|
|
|
LogcatLog(String tag) {
|
|
mTag = tag;
|
|
}
|
|
|
|
public static void enableVerboseLogging(boolean verboseEnabled) {
|
|
sVerboseLogging = verboseEnabled;
|
|
}
|
|
|
|
/* New-style methods */
|
|
@Override
|
|
public LogMessage err(String format) {
|
|
return new RealLogMessage(Log.ERROR, mTag, format);
|
|
}
|
|
|
|
@Override
|
|
public LogMessage warn(String format) {
|
|
return new RealLogMessage(Log.WARN, mTag, format);
|
|
}
|
|
|
|
@Override
|
|
public LogMessage info(String format) {
|
|
return new RealLogMessage(Log.INFO, mTag, format);
|
|
}
|
|
|
|
@Override
|
|
public LogMessage trace(String format) {
|
|
if (sVerboseLogging) {
|
|
return new RealLogMessage(Log.DEBUG, mTag, format,
|
|
getNameOfCallingMethod(0));
|
|
} else {
|
|
return NO_LOG_MESSAGE;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public LogMessage trace(String format, int numFramesToIgnore) {
|
|
if (sVerboseLogging) {
|
|
return new RealLogMessage(Log.DEBUG, mTag, format,
|
|
getNameOfCallingMethod(numFramesToIgnore));
|
|
} else {
|
|
return NO_LOG_MESSAGE;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public LogMessage dump(String format) {
|
|
if (sVerboseLogging) {
|
|
return new RealLogMessage(Log.VERBOSE, mTag, format);
|
|
} else {
|
|
return NO_LOG_MESSAGE;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void eC(String msg) {
|
|
Log.e(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void wC(String msg) {
|
|
Log.w(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void iC(String msg) {
|
|
Log.i(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void tC(String msg) {
|
|
Log.d(mTag, msg);
|
|
}
|
|
|
|
/* Legacy methods */
|
|
@Override
|
|
public void e(String msg) {
|
|
Log.e(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void w(String msg) {
|
|
Log.w(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void i(String msg) {
|
|
Log.i(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void d(String msg) {
|
|
Log.d(mTag, msg);
|
|
}
|
|
|
|
@Override
|
|
public void v(String msg) {
|
|
Log.v(mTag, msg);
|
|
}
|
|
|
|
/* Internal details */
|
|
private static class RealLogMessage implements WifiLog.LogMessage {
|
|
private final int mLogLevel;
|
|
private final String mTag;
|
|
private final String mFormat;
|
|
private final StringBuilder mStringBuilder;
|
|
private int mNextFormatCharPos;
|
|
|
|
RealLogMessage(int logLevel, String tag, String format) {
|
|
this(logLevel, tag, format, null);
|
|
}
|
|
|
|
RealLogMessage(int logLevel, String tag, String format, String prefix) {
|
|
mLogLevel = logLevel;
|
|
mTag = tag;
|
|
mFormat = format;
|
|
mStringBuilder = new StringBuilder();
|
|
mNextFormatCharPos = 0;
|
|
if (prefix != null) {
|
|
mStringBuilder.append(prefix).append(" ");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public WifiLog.LogMessage r(String value) {
|
|
// Since the logcat back-end is just transitional, we don't attempt to tag sensitive
|
|
// information in it.
|
|
return c(value);
|
|
}
|
|
|
|
@Override
|
|
public WifiLog.LogMessage c(String value) {
|
|
copyUntilPlaceholder();
|
|
if (mNextFormatCharPos < mFormat.length()) {
|
|
mStringBuilder.append(value);
|
|
++mNextFormatCharPos;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public WifiLog.LogMessage c(long value) {
|
|
copyUntilPlaceholder();
|
|
if (mNextFormatCharPos < mFormat.length()) {
|
|
mStringBuilder.append(value);
|
|
++mNextFormatCharPos;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public WifiLog.LogMessage c(char value) {
|
|
copyUntilPlaceholder();
|
|
if (mNextFormatCharPos < mFormat.length()) {
|
|
mStringBuilder.append(value);
|
|
++mNextFormatCharPos;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public WifiLog.LogMessage c(boolean value) {
|
|
copyUntilPlaceholder();
|
|
if (mNextFormatCharPos < mFormat.length()) {
|
|
mStringBuilder.append(value);
|
|
++mNextFormatCharPos;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public void flush() {
|
|
if (mNextFormatCharPos < mFormat.length()) {
|
|
mStringBuilder.append(mFormat, mNextFormatCharPos, mFormat.length());
|
|
}
|
|
Log.println(mLogLevel, mTag, mStringBuilder.toString());
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public String toString() {
|
|
return mStringBuilder.toString();
|
|
}
|
|
|
|
private void copyUntilPlaceholder() {
|
|
if (mNextFormatCharPos >= mFormat.length()) {
|
|
return;
|
|
}
|
|
|
|
int placeholderPos = mFormat.indexOf(WifiLog.PLACEHOLDER, mNextFormatCharPos);
|
|
if (placeholderPos == -1) {
|
|
placeholderPos = mFormat.length();
|
|
}
|
|
|
|
mStringBuilder.append(mFormat, mNextFormatCharPos, placeholderPos);
|
|
mNextFormatCharPos = placeholderPos;
|
|
}
|
|
}
|
|
|
|
private static final String[] TRACE_FRAMES_TO_IGNORE = {
|
|
"getNameOfCallingMethod()", "trace()"
|
|
};
|
|
private String getNameOfCallingMethod(int callerFramesToIgnore) {
|
|
final int frameNumOfInterest = callerFramesToIgnore + TRACE_FRAMES_TO_IGNORE.length;
|
|
// In some environments, it's much faster to get a stack trace from a Throwable
|
|
// https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6375302.
|
|
//
|
|
// While Dalvik optimizes the same-thread-stack-trace case,
|
|
// Throwable_nativeGetStackTrace() is still simpler than
|
|
// VMStack_getThreadStackTrace().
|
|
//
|
|
// Some crude benchmarking suggests that the cost of this approach is about
|
|
// 50 usec. go/logcatlog-trace-benchmark
|
|
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
|
|
try {
|
|
return stackTrace[frameNumOfInterest].getMethodName();
|
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
return ("<unknown>");
|
|
}
|
|
}
|
|
}
|