1 /* 2 * Copyright (C) 2016 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 android.util.Log; 20 21 import com.android.internal.annotations.Immutable; 22 import com.android.internal.annotations.VisibleForTesting; 23 24 import javax.annotation.concurrent.ThreadSafe; 25 26 /** 27 * Provides a WifiLog implementation which uses logd as the 28 * logging backend. 29 * 30 * This class is trivially thread-safe, as instances are immutable. 31 * Note, however, that LogMessage instances are _not_ thread-safe. 32 */ 33 @ThreadSafe 34 @Immutable 35 class LogcatLog implements WifiLog { 36 private final String mTag; 37 private static volatile boolean sVerboseLogging = false; 38 private static final NoLogMessage NO_LOG_MESSAGE = new NoLogMessage(); 39 LogcatLog(String tag)40 LogcatLog(String tag) { 41 mTag = tag; 42 } 43 enableVerboseLogging(int verboseMode)44 public static void enableVerboseLogging(int verboseMode) { 45 if (verboseMode > 0) { 46 sVerboseLogging = true; 47 } else { 48 sVerboseLogging = false; 49 } 50 } 51 52 /* New-style methods */ 53 @Override err(String format)54 public LogMessage err(String format) { 55 return new RealLogMessage(Log.ERROR, mTag, format); 56 } 57 58 @Override warn(String format)59 public LogMessage warn(String format) { 60 return new RealLogMessage(Log.WARN, mTag, format); 61 } 62 63 @Override info(String format)64 public LogMessage info(String format) { 65 return new RealLogMessage(Log.INFO, mTag, format); 66 } 67 68 @Override trace(String format)69 public LogMessage trace(String format) { 70 if (sVerboseLogging) { 71 return new RealLogMessage(Log.DEBUG, mTag, format, 72 getNameOfCallingMethod(0)); 73 } else { 74 return NO_LOG_MESSAGE; 75 } 76 } 77 78 @Override trace(String format, int numFramesToIgnore)79 public LogMessage trace(String format, int numFramesToIgnore) { 80 if (sVerboseLogging) { 81 return new RealLogMessage(Log.DEBUG, mTag, format, 82 getNameOfCallingMethod(numFramesToIgnore)); 83 } else { 84 return NO_LOG_MESSAGE; 85 } 86 } 87 88 @Override dump(String format)89 public LogMessage dump(String format) { 90 if (sVerboseLogging) { 91 return new RealLogMessage(Log.VERBOSE, mTag, format); 92 } else { 93 return NO_LOG_MESSAGE; 94 } 95 } 96 97 @Override eC(String msg)98 public void eC(String msg) { 99 Log.e(mTag, msg); 100 } 101 102 @Override wC(String msg)103 public void wC(String msg) { 104 Log.w(mTag, msg); 105 } 106 107 @Override iC(String msg)108 public void iC(String msg) { 109 Log.i(mTag, msg); 110 } 111 112 @Override tC(String msg)113 public void tC(String msg) { 114 Log.d(mTag, msg); 115 } 116 117 /* Legacy methods */ 118 @Override e(String msg)119 public void e(String msg) { 120 Log.e(mTag, msg); 121 } 122 123 @Override w(String msg)124 public void w(String msg) { 125 Log.w(mTag, msg); 126 } 127 128 @Override i(String msg)129 public void i(String msg) { 130 Log.i(mTag, msg); 131 } 132 133 @Override d(String msg)134 public void d(String msg) { 135 Log.d(mTag, msg); 136 } 137 138 @Override v(String msg)139 public void v(String msg) { 140 Log.v(mTag, msg); 141 } 142 143 /* Internal details */ 144 private static class RealLogMessage implements WifiLog.LogMessage { 145 private final int mLogLevel; 146 private final String mTag; 147 private final String mFormat; 148 private final StringBuilder mStringBuilder; 149 private int mNextFormatCharPos; 150 RealLogMessage(int logLevel, String tag, String format)151 RealLogMessage(int logLevel, String tag, String format) { 152 this(logLevel, tag, format, null); 153 } 154 RealLogMessage(int logLevel, String tag, String format, String prefix)155 RealLogMessage(int logLevel, String tag, String format, String prefix) { 156 mLogLevel = logLevel; 157 mTag = tag; 158 mFormat = format; 159 mStringBuilder = new StringBuilder(); 160 mNextFormatCharPos = 0; 161 if (prefix != null) { 162 mStringBuilder.append(prefix).append(" "); 163 } 164 } 165 166 @Override r(String value)167 public WifiLog.LogMessage r(String value) { 168 // Since the logcat back-end is just transitional, we don't attempt to tag sensitive 169 // information in it. 170 return c(value); 171 } 172 173 @Override c(String value)174 public WifiLog.LogMessage c(String value) { 175 copyUntilPlaceholder(); 176 if (mNextFormatCharPos < mFormat.length()) { 177 mStringBuilder.append(value); 178 ++mNextFormatCharPos; 179 } 180 return this; 181 } 182 183 @Override c(long value)184 public WifiLog.LogMessage c(long value) { 185 copyUntilPlaceholder(); 186 if (mNextFormatCharPos < mFormat.length()) { 187 mStringBuilder.append(value); 188 ++mNextFormatCharPos; 189 } 190 return this; 191 } 192 193 @Override c(char value)194 public WifiLog.LogMessage c(char value) { 195 copyUntilPlaceholder(); 196 if (mNextFormatCharPos < mFormat.length()) { 197 mStringBuilder.append(value); 198 ++mNextFormatCharPos; 199 } 200 return this; 201 } 202 203 @Override c(boolean value)204 public WifiLog.LogMessage c(boolean value) { 205 copyUntilPlaceholder(); 206 if (mNextFormatCharPos < mFormat.length()) { 207 mStringBuilder.append(value); 208 ++mNextFormatCharPos; 209 } 210 return this; 211 } 212 213 @Override flush()214 public void flush() { 215 if (mNextFormatCharPos < mFormat.length()) { 216 mStringBuilder.append(mFormat, mNextFormatCharPos, mFormat.length()); 217 } 218 Log.println(mLogLevel, mTag, mStringBuilder.toString()); 219 } 220 221 @VisibleForTesting toString()222 public String toString() { 223 return mStringBuilder.toString(); 224 } 225 copyUntilPlaceholder()226 private void copyUntilPlaceholder() { 227 if (mNextFormatCharPos >= mFormat.length()) { 228 return; 229 } 230 231 int placeholderPos = mFormat.indexOf(WifiLog.PLACEHOLDER, mNextFormatCharPos); 232 if (placeholderPos == -1) { 233 placeholderPos = mFormat.length(); 234 } 235 236 mStringBuilder.append(mFormat, mNextFormatCharPos, placeholderPos); 237 mNextFormatCharPos = placeholderPos; 238 } 239 } 240 241 private static final String[] TRACE_FRAMES_TO_IGNORE = { 242 "getNameOfCallingMethod()", "trace()" 243 }; getNameOfCallingMethod(int callerFramesToIgnore)244 private String getNameOfCallingMethod(int callerFramesToIgnore) { 245 final int frameNumOfInterest = callerFramesToIgnore + TRACE_FRAMES_TO_IGNORE.length; 246 // In some environments, it's much faster to get a stack trace from a Throwable 247 // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6375302. 248 // 249 // While Dalvik optimizes the same-thread-stack-trace case, 250 // Throwable_nativeGetStackTrace() is still simpler than 251 // VMStack_getThreadStackTrace(). 252 // 253 // Some crude benchmarking suggests that the cost of this approach is about 254 // 50 usec. go/logcatlog-trace-benchmark 255 StackTraceElement[] stackTrace = (new Throwable()).getStackTrace(); 256 try { 257 return stackTrace[frameNumOfInterest].getMethodName(); 258 } catch (ArrayIndexOutOfBoundsException e) { 259 return ("<unknown>"); 260 } 261 } 262 } 263