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