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
17package com.android.server.wifi;
18
19import android.util.Log;
20
21import com.android.internal.annotations.Immutable;
22
23import javax.annotation.concurrent.ThreadSafe;
24
25/**
26 * Provides a WifiLog implementation which uses logd as the
27 * logging backend.
28 *
29 * This class is trivially thread-safe, as instances are immutable.
30 * Note, however, that LogMessage instances are _not_ thread-safe.
31 */
32@ThreadSafe
33@Immutable
34class LogcatLog implements WifiLog {
35    private final String mTag;
36    private static volatile boolean sVerboseLogging = false;
37
38    LogcatLog(String tag) {
39        mTag = tag;
40    }
41
42    public static void enableVerboseLogging(int verboseMode) {
43        if (verboseMode > 0) {
44            sVerboseLogging = true;
45        } else {
46            sVerboseLogging = false;
47        }
48    }
49
50    /* New-style methods */
51    @Override
52    public LogMessage err(String format) {
53        return makeLogMessage(Log.ERROR, format);
54    }
55
56    @Override
57    public LogMessage warn(String format) {
58        return makeLogMessage(Log.WARN, format);
59    }
60
61    @Override
62    public LogMessage info(String format) {
63        return makeLogMessage(Log.INFO, format);
64    }
65
66    @Override
67    public LogMessage trace(String format) {
68        return makeLogMessage(Log.DEBUG, format);
69    }
70
71    @Override
72    public LogMessage dump(String format) {
73        return makeLogMessage(Log.VERBOSE, format);
74    }
75
76    @Override
77    public void eC(String msg) {
78        Log.e(mTag, msg);
79    }
80
81    @Override
82    public void wC(String msg) {
83        Log.w(mTag, msg);
84    }
85
86    @Override
87    public void iC(String msg) {
88        Log.i(mTag, msg);
89    }
90
91    @Override
92    public void tC(String msg) {
93        Log.d(mTag, msg);
94    }
95
96    /* Legacy methods */
97    @Override
98    public void e(String msg) {
99        Log.e(mTag, msg);
100    }
101
102    @Override
103    public void w(String msg) {
104        Log.w(mTag, msg);
105    }
106
107    @Override
108    public void i(String msg) {
109        Log.i(mTag, msg);
110    }
111
112    @Override
113    public void d(String msg) {
114        Log.d(mTag, msg);
115    }
116
117    @Override
118    public void v(String msg) {
119        Log.v(mTag, msg);
120    }
121
122    /* Internal details */
123    private static class RealLogMessage implements WifiLog.LogMessage {
124        private final int mLogLevel;
125        private final String mTag;
126        private final String mFormat;
127        private final StringBuilder mStringBuilder;
128        private int mNextFormatCharPos;
129
130        RealLogMessage(int logLevel, String tag, String format) {
131            mLogLevel = logLevel;
132            mTag = tag;
133            mFormat = format;
134            mStringBuilder = new StringBuilder();
135            mNextFormatCharPos = 0;
136        }
137
138        @Override
139        public WifiLog.LogMessage r(String value) {
140            // Since the logcat back-end is just transitional, we don't attempt to tag sensitive
141            // information in it.
142            return c(value);
143        }
144
145        @Override
146        public WifiLog.LogMessage c(String value) {
147            copyUntilPlaceholder();
148            if (mNextFormatCharPos < mFormat.length()) {
149                mStringBuilder.append(value);
150                ++mNextFormatCharPos;
151            }
152            return this;
153        }
154
155        @Override
156        public WifiLog.LogMessage c(long value) {
157            copyUntilPlaceholder();
158            if (mNextFormatCharPos < mFormat.length()) {
159                mStringBuilder.append(value);
160                ++mNextFormatCharPos;
161            }
162            return this;
163        }
164
165        @Override
166        public WifiLog.LogMessage c(char value) {
167            copyUntilPlaceholder();
168            if (mNextFormatCharPos < mFormat.length()) {
169                mStringBuilder.append(value);
170                ++mNextFormatCharPos;
171            }
172            return this;
173        }
174
175        @Override
176        public WifiLog.LogMessage c(boolean value) {
177            copyUntilPlaceholder();
178            if (mNextFormatCharPos < mFormat.length()) {
179                mStringBuilder.append(value);
180                ++mNextFormatCharPos;
181            }
182            return this;
183        }
184
185        @Override
186        public void flush() {
187            if (mNextFormatCharPos < mFormat.length()) {
188                mStringBuilder.append(mFormat, mNextFormatCharPos, mFormat.length());
189            }
190            if (sVerboseLogging || mLogLevel > Log.DEBUG) {
191                Log.println(mLogLevel, mTag, mStringBuilder.toString());
192            }
193        }
194
195        /* Should generally not be used; implemented primarily to aid in testing. */
196        public String toString() {
197            return mStringBuilder.toString();
198        }
199
200        private void copyUntilPlaceholder() {
201            if (mNextFormatCharPos >= mFormat.length()) {
202                return;
203            }
204
205            int placeholderPos = mFormat.indexOf(WifiLog.PLACEHOLDER, mNextFormatCharPos);
206            if (placeholderPos == -1) {
207                placeholderPos = mFormat.length();
208            }
209
210            mStringBuilder.append(mFormat, mNextFormatCharPos, placeholderPos);
211            mNextFormatCharPos = placeholderPos;
212        }
213    }
214
215    private LogMessage makeLogMessage(int logLevel, String format) {
216        // TODO(b/30737821): Consider adding an isLoggable() check.
217        return new RealLogMessage(logLevel, mTag, format);
218    }
219}
220