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