blob: ce296009f598305e071f247e6d31cfefeb2c8169 [file] [log] [blame]
/*
* 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 DummyLogMessage sDummyLogMessage = new DummyLogMessage();
LogcatLog(String tag) {
mTag = tag;
}
public static void enableVerboseLogging(int verboseMode) {
if (verboseMode > 0) {
sVerboseLogging = true;
} else {
sVerboseLogging = false;
}
}
/* 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 sDummyLogMessage;
}
}
@Override
public LogMessage trace(String format, int numFramesToIgnore) {
if (sVerboseLogging) {
return new RealLogMessage(Log.DEBUG, mTag, format,
getNameOfCallingMethod(numFramesToIgnore));
} else {
return sDummyLogMessage;
}
}
@Override
public LogMessage dump(String format) {
if (sVerboseLogging) {
return new RealLogMessage(Log.VERBOSE, mTag, format);
} else {
return sDummyLogMessage;
}
}
@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>");
}
}
}