blob: 01af76b4ce4e3d23cf6fc0b295e40df49ae5dc8e [file] [log] [blame]
Ihab Awad60ac30b2014-05-20 22:32:12 -07001/*
2 * Copyright 2014, 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
Tyler Gunnef9f6f92014-09-12 22:16:17 -070017package android.telecom;
Ihab Awad60ac30b2014-05-20 22:32:12 -070018
Santos Cordon3c20d632016-02-25 16:12:35 -080019import android.net.Uri;
Hall Liue362e502016-01-07 17:35:54 -080020import android.os.AsyncTask;
Brad Ebinger51b98342016-09-22 16:30:46 -070021import android.telecom.Logging.EventManager;
22import android.telecom.Logging.Session;
23import android.telecom.Logging.SessionManager;
Santos Cordon3c20d632016-02-25 16:12:35 -080024import android.telephony.PhoneNumberUtils;
25import android.text.TextUtils;
Hall Liue362e502016-01-07 17:35:54 -080026
Brad Ebinger51b98342016-09-22 16:30:46 -070027import com.android.internal.annotations.VisibleForTesting;
28import com.android.internal.util.IndentingPrintWriter;
29
Ihab Awad60ac30b2014-05-20 22:32:12 -070030import java.security.MessageDigest;
31import java.security.NoSuchAlgorithmException;
32import java.util.IllegalFormatException;
33import java.util.Locale;
34
35/**
36 * Manages logging for the entire module.
37 *
38 * @hide
39 */
Brad Ebinger51b98342016-09-22 16:30:46 -070040public class Log {
Ihab Awad60ac30b2014-05-20 22:32:12 -070041
Brad Ebinger51b98342016-09-22 16:30:46 -070042 private static final long EXTENDED_LOGGING_DURATION_MILLIS = 60000 * 30; // 30 minutes
Ihab Awad60ac30b2014-05-20 22:32:12 -070043
Brad Ebinger51b98342016-09-22 16:30:46 -070044 private static final int EVENTS_TO_CACHE = 10;
45 private static final int EVENTS_TO_CACHE_DEBUG = 20;
46
47 // Generic tag for all Telecom logging
48 @VisibleForTesting
49 public static String TAG = "TelecomFramework";
50
51 private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
Ihab Awad60ac30b2014-05-20 22:32:12 -070052 public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
53 public static final boolean INFO = isLoggable(android.util.Log.INFO);
54 public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
55 public static final boolean WARN = isLoggable(android.util.Log.WARN);
56 public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
57
Brad Ebinger51b98342016-09-22 16:30:46 -070058 // Used to synchronize singleton logging lazy initialization
59 private static final Object sSingletonSync = new Object();
60 private static EventManager sEventManager;
61 private static SessionManager sSessionManager;
62
63 /**
64 * Tracks whether user-activated extended logging is enabled.
65 */
66 private static boolean sIsUserExtendedLoggingEnabled = false;
67
68 /**
69 * The time when user-activated extended logging should be ended. Used to determine when
70 * extended logging should automatically be disabled.
71 */
72 private static long sUserExtendedLoggingStopTime = 0;
73
74 private Log() {
75 }
76
77 public static void d(String prefix, String format, Object... args) {
78 if (sIsUserExtendedLoggingEnabled) {
79 maybeDisableLogging();
80 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
81 } else if (DEBUG) {
82 android.util.Slog.d(TAG, buildMessage(prefix, format, args));
83 }
84 }
85
86 public static void d(Object objectPrefix, String format, Object... args) {
87 if (sIsUserExtendedLoggingEnabled) {
88 maybeDisableLogging();
89 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
90 } else if (DEBUG) {
91 android.util.Slog.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
92 }
93 }
94
95 public static void i(String prefix, String format, Object... args) {
96 if (INFO) {
97 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
98 }
99 }
100
101 public static void i(Object objectPrefix, String format, Object... args) {
102 if (INFO) {
103 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
104 }
105 }
106
107 public static void v(String prefix, String format, Object... args) {
108 if (sIsUserExtendedLoggingEnabled) {
109 maybeDisableLogging();
110 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
111 } else if (VERBOSE) {
112 android.util.Slog.v(TAG, buildMessage(prefix, format, args));
113 }
114 }
115
116 public static void v(Object objectPrefix, String format, Object... args) {
117 if (sIsUserExtendedLoggingEnabled) {
118 maybeDisableLogging();
119 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
120 } else if (VERBOSE) {
121 android.util.Slog.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
122 }
123 }
124
125 public static void w(String prefix, String format, Object... args) {
126 if (WARN) {
127 android.util.Slog.w(TAG, buildMessage(prefix, format, args));
128 }
129 }
130
131 public static void w(Object objectPrefix, String format, Object... args) {
132 if (WARN) {
133 android.util.Slog.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
134 }
135 }
136
137 public static void e(String prefix, Throwable tr, String format, Object... args) {
138 if (ERROR) {
139 android.util.Slog.e(TAG, buildMessage(prefix, format, args), tr);
140 }
141 }
142
143 public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
144 if (ERROR) {
145 android.util.Slog.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
146 tr);
147 }
148 }
149
150 public static void wtf(String prefix, Throwable tr, String format, Object... args) {
151 android.util.Slog.wtf(TAG, buildMessage(prefix, format, args), tr);
152 }
153
154 public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
155 android.util.Slog.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
156 tr);
157 }
158
159 public static void wtf(String prefix, String format, Object... args) {
160 String msg = buildMessage(prefix, format, args);
161 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
162 }
163
164 public static void wtf(Object objectPrefix, String format, Object... args) {
165 String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
166 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
167 }
168
169 /**
170 * The ease of use methods below only act mostly as proxies to the Session and Event Loggers.
171 * They also control the lazy loaders of the singleton instances, which will never be loaded if
172 * the proxy methods aren't used.
173 *
174 * Please see each method's documentation inside of their respective implementations in the
175 * loggers.
176 */
177
178 public static void startSession(String shortMethodName) {
179 getSessionManager().startSession(shortMethodName, null);
180 }
181
182 public static void startSession(String shortMethodName, String callerIdentification) {
183 getSessionManager().startSession(shortMethodName, callerIdentification);
184 }
185
186 public static Session createSubsession() {
187 return getSessionManager().createSubsession();
188 }
189
190 public static void cancelSubsession(Session subsession) {
191 getSessionManager().cancelSubsession(subsession);
192 }
193
194 public static void continueSession(Session subsession, String shortMethodName) {
195 getSessionManager().continueSession(subsession, shortMethodName);
196 }
197
198 public static void endSession() {
199 getSessionManager().endSession();
200 }
201
Brad Ebinger836efad2016-10-18 13:48:17 -0700202 public static void registerSessionListener(SessionManager.ISessionListener l) {
203 getSessionManager().registerSessionListener(l);
204 }
205
Brad Ebinger51b98342016-09-22 16:30:46 -0700206 public static String getSessionId() {
207 // If the Session logger has not been initialized, then there have been no sessions logged.
208 // Don't load it now!
209 synchronized (sSingletonSync) {
210 if (sSessionManager != null) {
211 return getSessionManager().getSessionId();
212 } else {
213 return "";
214 }
215 }
216 }
217
218 public static void addEvent(EventManager.Loggable recordEntry, String event) {
219 getEventManager().event(recordEntry, event, null);
220 }
221
222 public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
223 getEventManager().event(recordEntry, event, data);
224 }
225
226 public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
227 Object... args) {
228 getEventManager().event(recordEntry, event, format, args);
229 }
230
231 public static void registerEventListener(EventManager.EventListener e) {
232 getEventManager().registerEventListener(e);
233 }
234
235 public static void addRequestResponsePair(EventManager.TimedEventPair p) {
236 getEventManager().addRequestResponsePair(p);
237 }
238
239 public static void dumpEvents(IndentingPrintWriter pw) {
240 // If the Events logger has not been initialized, then there have been no events logged.
241 // Don't load it now!
242 synchronized (sSingletonSync) {
243 if (sEventManager != null) {
244 getEventManager().dumpEvents(pw);
245 } else {
246 pw.println("No Historical Events Logged.");
247 }
248 }
249 }
250
251 /**
252 * Enable or disable extended telecom logging.
253 *
254 * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
255 * {@code false} if it should be disabled.
256 */
257 public static void setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled) {
258 // If the state hasn't changed, bail early.
259 if (sIsUserExtendedLoggingEnabled == isExtendedLoggingEnabled) {
260 return;
261 }
262
263 if (sEventManager != null) {
264 sEventManager.changeEventCacheSize(isExtendedLoggingEnabled ?
265 EVENTS_TO_CACHE_DEBUG : EVENTS_TO_CACHE);
266 }
267
268 sIsUserExtendedLoggingEnabled = isExtendedLoggingEnabled;
269 if (sIsUserExtendedLoggingEnabled) {
270 sUserExtendedLoggingStopTime = System.currentTimeMillis()
271 + EXTENDED_LOGGING_DURATION_MILLIS;
272 } else {
273 sUserExtendedLoggingStopTime = 0;
274 }
275 }
276
277 private static EventManager getEventManager() {
278 // Checking for null again outside of synchronization because we only need to synchronize
279 // during the lazy loading of the events logger. We don't need to synchronize elsewhere.
280 if (sEventManager == null) {
281 synchronized (sSingletonSync) {
282 if (sEventManager == null) {
283 sEventManager = new EventManager(Log::getSessionId);
284 return sEventManager;
285 }
286 }
287 }
288 return sEventManager;
289 }
290
291 private static SessionManager getSessionManager() {
292 // Checking for null again outside of synchronization because we only need to synchronize
293 // during the lazy loading of the session logger. We don't need to synchronize elsewhere.
294 if (sSessionManager == null) {
295 synchronized (sSingletonSync) {
296 if (sSessionManager == null) {
297 sSessionManager = new SessionManager();
298 return sSessionManager;
299 }
300 }
301 }
302 return sSessionManager;
303 }
304
Hall Liue362e502016-01-07 17:35:54 -0800305 private static MessageDigest sMessageDigest;
306
Brad Ebinger51b98342016-09-22 16:30:46 -0700307 static void initMd5Sum() {
Hall Liue362e502016-01-07 17:35:54 -0800308 new AsyncTask<Void, Void, Void>() {
309 @Override
310 public Void doInBackground(Void... args) {
311 MessageDigest md;
312 try {
313 md = MessageDigest.getInstance("SHA-1");
314 } catch (NoSuchAlgorithmException e) {
315 md = null;
316 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700317 sMessageDigest = md;
Hall Liue362e502016-01-07 17:35:54 -0800318 return null;
319 }
320 }.execute();
321 }
322
Brad Ebinger51b98342016-09-22 16:30:46 -0700323 public static void setTag(String tag) {
324 TAG = tag;
325 }
326
327 /**
328 * If user enabled extended logging is enabled and the time limit has passed, disables the
329 * extended logging.
330 */
331 private static void maybeDisableLogging() {
332 if (!sIsUserExtendedLoggingEnabled) {
333 return;
334 }
335
336 if (sUserExtendedLoggingStopTime < System.currentTimeMillis()) {
337 sUserExtendedLoggingStopTime = 0;
338 sIsUserExtendedLoggingEnabled = false;
339 }
340 }
341
Ihab Awad60ac30b2014-05-20 22:32:12 -0700342 public static boolean isLoggable(int level) {
343 return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
344 }
345
Brad Ebinger51b98342016-09-22 16:30:46 -0700346 public static String piiHandle(Object pii) {
347 if (pii == null || VERBOSE) {
348 return String.valueOf(pii);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700349 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700350
Brad Ebinger51b98342016-09-22 16:30:46 -0700351 StringBuilder sb = new StringBuilder();
352 if (pii instanceof Uri) {
353 Uri uri = (Uri) pii;
354 String scheme = uri.getScheme();
355
356 if (!TextUtils.isEmpty(scheme)) {
357 sb.append(scheme).append(":");
358 }
359
360 String textToObfuscate = uri.getSchemeSpecificPart();
361 if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
362 for (int i = 0; i < textToObfuscate.length(); i++) {
363 char c = textToObfuscate.charAt(i);
364 sb.append(PhoneNumberUtils.isDialable(c) ? "*" : c);
365 }
366 } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
367 for (int i = 0; i < textToObfuscate.length(); i++) {
368 char c = textToObfuscate.charAt(i);
369 if (c != '@' && c != '.') {
370 c = '*';
371 }
372 sb.append(c);
373 }
374 } else {
375 sb.append(pii(pii));
376 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700377 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700378
Brad Ebinger51b98342016-09-22 16:30:46 -0700379 return sb.toString();
Ihab Awad60ac30b2014-05-20 22:32:12 -0700380 }
381
382 /**
383 * Redact personally identifiable information for production users.
384 * If we are running in verbose mode, return the original string, otherwise
385 * return a SHA-1 hash of the input string.
386 */
387 public static String pii(Object pii) {
388 if (pii == null || VERBOSE) {
389 return String.valueOf(pii);
390 }
391 return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
392 }
393
394 private static String secureHash(byte[] input) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700395 if (sMessageDigest != null) {
396 sMessageDigest.reset();
397 sMessageDigest.update(input);
398 byte[] result = sMessageDigest.digest();
399 return encodeHex(result);
400 } else {
401 return "Uninitialized SHA1";
Ihab Awad60ac30b2014-05-20 22:32:12 -0700402 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700403 }
404
405 private static String encodeHex(byte[] bytes) {
406 StringBuffer hex = new StringBuffer(bytes.length * 2);
407
408 for (int i = 0; i < bytes.length; i++) {
409 int byteIntValue = bytes[i] & 0xff;
410 if (byteIntValue < 0x10) {
411 hex.append("0");
412 }
413 hex.append(Integer.toString(byteIntValue, 16));
414 }
415
416 return hex.toString();
417 }
418
419 private static String getPrefixFromObject(Object obj) {
420 return obj == null ? "<null>" : obj.getClass().getSimpleName();
421 }
422
423 private static String buildMessage(String prefix, String format, Object... args) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700424 // Incorporate thread ID and calling method into prefix
425 String sessionName = getSessionId();
426 String sessionPostfix = TextUtils.isEmpty(sessionName) ? "" : ": " + sessionName;
427
Ihab Awad60ac30b2014-05-20 22:32:12 -0700428 String msg;
429 try {
430 msg = (args == null || args.length == 0) ? format
431 : String.format(Locale.US, format, args);
432 } catch (IllegalFormatException ife) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700433 e("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
Ihab Awad60ac30b2014-05-20 22:32:12 -0700434 args.length);
435 msg = format + " (An error occurred while formatting the message.)";
436 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700437 return String.format(Locale.US, "%s: %s%s", prefix, msg, sessionPostfix);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700438 }
439}