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