blob: de205380c3a7502ffd0d35563729810418611fee [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;
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +090022import android.os.Build;
Brad Ebinger51b98342016-09-22 16:30:46 -070023import android.telecom.Logging.EventManager;
24import android.telecom.Logging.Session;
25import android.telecom.Logging.SessionManager;
Santos Cordon3c20d632016-02-25 16:12:35 -080026import android.telephony.PhoneNumberUtils;
27import android.text.TextUtils;
Hall Liue362e502016-01-07 17:35:54 -080028
Brad Ebinger51b98342016-09-22 16:30:46 -070029import com.android.internal.annotations.VisibleForTesting;
30import com.android.internal.util.IndentingPrintWriter;
31
Ihab Awad60ac30b2014-05-20 22:32:12 -070032import java.security.MessageDigest;
33import java.security.NoSuchAlgorithmException;
34import java.util.IllegalFormatException;
35import java.util.Locale;
36
37/**
38 * Manages logging for the entire module.
39 *
40 * @hide
41 */
Brad Ebinger51b98342016-09-22 16:30:46 -070042public class Log {
Ihab Awad60ac30b2014-05-20 22:32:12 -070043
Brad Ebinger51b98342016-09-22 16:30:46 -070044 private static final long EXTENDED_LOGGING_DURATION_MILLIS = 60000 * 30; // 30 minutes
Ihab Awad60ac30b2014-05-20 22:32:12 -070045
Brad Ebinger51b98342016-09-22 16:30:46 -070046 private static final int EVENTS_TO_CACHE = 10;
47 private static final int EVENTS_TO_CACHE_DEBUG = 20;
48
49 // Generic tag for all Telecom logging
50 @VisibleForTesting
51 public static String TAG = "TelecomFramework";
Brad Ebinger0c3541b2016-11-01 14:11:38 -070052 public static boolean DEBUG = isLoggable(android.util.Log.DEBUG);
53 public static boolean INFO = isLoggable(android.util.Log.INFO);
54 public static boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
55 public static boolean WARN = isLoggable(android.util.Log.WARN);
56 public static boolean ERROR = isLoggable(android.util.Log.ERROR);
Brad Ebinger51b98342016-09-22 16:30:46 -070057
58 private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +090059 private static final boolean USER_BUILD = Build.TYPE.equals("user");
Ihab Awad60ac30b2014-05-20 22:32:12 -070060
Brad Ebinger51b98342016-09-22 16:30:46 -070061 // Used to synchronize singleton logging lazy initialization
62 private static final Object sSingletonSync = new Object();
63 private static EventManager sEventManager;
64 private static SessionManager sSessionManager;
65
66 /**
67 * Tracks whether user-activated extended logging is enabled.
68 */
69 private static boolean sIsUserExtendedLoggingEnabled = false;
70
71 /**
72 * The time when user-activated extended logging should be ended. Used to determine when
73 * extended logging should automatically be disabled.
74 */
75 private static long sUserExtendedLoggingStopTime = 0;
76
77 private Log() {
78 }
79
80 public static void d(String prefix, String format, Object... args) {
81 if (sIsUserExtendedLoggingEnabled) {
82 maybeDisableLogging();
83 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
84 } else if (DEBUG) {
85 android.util.Slog.d(TAG, buildMessage(prefix, format, args));
86 }
87 }
88
89 public static void d(Object objectPrefix, String format, Object... args) {
90 if (sIsUserExtendedLoggingEnabled) {
91 maybeDisableLogging();
92 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
93 } else if (DEBUG) {
94 android.util.Slog.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
95 }
96 }
97
98 public static void i(String prefix, String format, Object... args) {
99 if (INFO) {
100 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
101 }
102 }
103
104 public static void i(Object objectPrefix, String format, Object... args) {
105 if (INFO) {
106 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
107 }
108 }
109
110 public static void v(String prefix, String format, Object... args) {
111 if (sIsUserExtendedLoggingEnabled) {
112 maybeDisableLogging();
113 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
114 } else if (VERBOSE) {
115 android.util.Slog.v(TAG, buildMessage(prefix, format, args));
116 }
117 }
118
119 public static void v(Object objectPrefix, String format, Object... args) {
120 if (sIsUserExtendedLoggingEnabled) {
121 maybeDisableLogging();
122 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
123 } else if (VERBOSE) {
124 android.util.Slog.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
125 }
126 }
127
128 public static void w(String prefix, String format, Object... args) {
129 if (WARN) {
130 android.util.Slog.w(TAG, buildMessage(prefix, format, args));
131 }
132 }
133
134 public static void w(Object objectPrefix, String format, Object... args) {
135 if (WARN) {
136 android.util.Slog.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
137 }
138 }
139
140 public static void e(String prefix, Throwable tr, String format, Object... args) {
141 if (ERROR) {
142 android.util.Slog.e(TAG, buildMessage(prefix, format, args), tr);
143 }
144 }
145
146 public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
147 if (ERROR) {
148 android.util.Slog.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
149 tr);
150 }
151 }
152
153 public static void wtf(String prefix, Throwable tr, String format, Object... args) {
154 android.util.Slog.wtf(TAG, buildMessage(prefix, format, args), tr);
155 }
156
157 public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
158 android.util.Slog.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
159 tr);
160 }
161
162 public static void wtf(String prefix, String format, Object... args) {
163 String msg = buildMessage(prefix, format, args);
164 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
165 }
166
167 public static void wtf(Object objectPrefix, String format, Object... args) {
168 String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
169 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
170 }
171
172 /**
173 * The ease of use methods below only act mostly as proxies to the Session and Event Loggers.
174 * They also control the lazy loaders of the singleton instances, which will never be loaded if
175 * the proxy methods aren't used.
176 *
177 * Please see each method's documentation inside of their respective implementations in the
178 * loggers.
179 */
180
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700181 public static void setSessionContext(Context context) {
182 getSessionManager().setContext(context);
183 }
184
Brad Ebinger51b98342016-09-22 16:30:46 -0700185 public static void startSession(String shortMethodName) {
186 getSessionManager().startSession(shortMethodName, null);
187 }
188
Brad Ebinger3445f822016-10-24 16:40:49 -0700189 public static void startSession(Session.Info info, String shortMethodName) {
190 getSessionManager().startSession(info, shortMethodName, null);
191 }
192
Brad Ebinger51b98342016-09-22 16:30:46 -0700193 public static void startSession(String shortMethodName, String callerIdentification) {
194 getSessionManager().startSession(shortMethodName, callerIdentification);
195 }
196
Brad Ebingera0dc9762016-10-21 09:41:29 -0700197 public static void startSession(Session.Info info, String shortMethodName,
198 String callerIdentification) {
199 getSessionManager().startSession(info, shortMethodName, callerIdentification);
200 }
201
Brad Ebinger51b98342016-09-22 16:30:46 -0700202 public static Session createSubsession() {
203 return getSessionManager().createSubsession();
204 }
205
Brad Ebinger3445f822016-10-24 16:40:49 -0700206 public static Session.Info getExternalSession() {
207 return getSessionManager().getExternalSession();
208 }
209
Brad Ebinger51b98342016-09-22 16:30:46 -0700210 public static void cancelSubsession(Session subsession) {
211 getSessionManager().cancelSubsession(subsession);
212 }
213
214 public static void continueSession(Session subsession, String shortMethodName) {
215 getSessionManager().continueSession(subsession, shortMethodName);
216 }
217
218 public static void endSession() {
219 getSessionManager().endSession();
220 }
221
Brad Ebinger836efad2016-10-18 13:48:17 -0700222 public static void registerSessionListener(SessionManager.ISessionListener l) {
223 getSessionManager().registerSessionListener(l);
224 }
225
Brad Ebinger51b98342016-09-22 16:30:46 -0700226 public static String getSessionId() {
227 // If the Session logger has not been initialized, then there have been no sessions logged.
228 // Don't load it now!
229 synchronized (sSingletonSync) {
230 if (sSessionManager != null) {
231 return getSessionManager().getSessionId();
232 } else {
233 return "";
234 }
235 }
236 }
237
238 public static void addEvent(EventManager.Loggable recordEntry, String event) {
239 getEventManager().event(recordEntry, event, null);
240 }
241
242 public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
243 getEventManager().event(recordEntry, event, data);
244 }
245
246 public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
247 Object... args) {
248 getEventManager().event(recordEntry, event, format, args);
249 }
250
251 public static void registerEventListener(EventManager.EventListener e) {
252 getEventManager().registerEventListener(e);
253 }
254
255 public static void addRequestResponsePair(EventManager.TimedEventPair p) {
256 getEventManager().addRequestResponsePair(p);
257 }
258
259 public static void dumpEvents(IndentingPrintWriter pw) {
260 // If the Events logger has not been initialized, then there have been no events logged.
261 // Don't load it now!
262 synchronized (sSingletonSync) {
263 if (sEventManager != null) {
264 getEventManager().dumpEvents(pw);
265 } else {
266 pw.println("No Historical Events Logged.");
267 }
268 }
269 }
270
271 /**
Tyler Gunn2db81b52017-05-19 10:10:23 -0700272 * Dumps the events in a timeline format.
273 * @param pw The {@link IndentingPrintWriter} to write to.
274 * @hide
275 */
276 public static void dumpEventsTimeline(IndentingPrintWriter pw) {
277 // If the Events logger has not been initialized, then there have been no events logged.
278 // Don't load it now!
279 synchronized (sSingletonSync) {
280 if (sEventManager != null) {
281 getEventManager().dumpEventsTimeline(pw);
282 } else {
283 pw.println("No Historical Events Logged.");
284 }
285 }
286 }
287
288 /**
Brad Ebinger51b98342016-09-22 16:30:46 -0700289 * Enable or disable extended telecom logging.
290 *
291 * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
292 * {@code false} if it should be disabled.
293 */
294 public static void setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled) {
295 // If the state hasn't changed, bail early.
296 if (sIsUserExtendedLoggingEnabled == isExtendedLoggingEnabled) {
297 return;
298 }
299
300 if (sEventManager != null) {
301 sEventManager.changeEventCacheSize(isExtendedLoggingEnabled ?
302 EVENTS_TO_CACHE_DEBUG : EVENTS_TO_CACHE);
303 }
304
305 sIsUserExtendedLoggingEnabled = isExtendedLoggingEnabled;
306 if (sIsUserExtendedLoggingEnabled) {
307 sUserExtendedLoggingStopTime = System.currentTimeMillis()
308 + EXTENDED_LOGGING_DURATION_MILLIS;
309 } else {
310 sUserExtendedLoggingStopTime = 0;
311 }
312 }
313
314 private static EventManager getEventManager() {
315 // Checking for null again outside of synchronization because we only need to synchronize
316 // during the lazy loading of the events logger. We don't need to synchronize elsewhere.
317 if (sEventManager == null) {
318 synchronized (sSingletonSync) {
319 if (sEventManager == null) {
320 sEventManager = new EventManager(Log::getSessionId);
321 return sEventManager;
322 }
323 }
324 }
325 return sEventManager;
326 }
327
Brad Ebinger8adafe72017-06-08 15:44:40 -0700328 @VisibleForTesting
329 public static SessionManager getSessionManager() {
Brad Ebinger51b98342016-09-22 16:30:46 -0700330 // Checking for null again outside of synchronization because we only need to synchronize
331 // during the lazy loading of the session logger. We don't need to synchronize elsewhere.
332 if (sSessionManager == null) {
333 synchronized (sSingletonSync) {
334 if (sSessionManager == null) {
335 sSessionManager = new SessionManager();
336 return sSessionManager;
337 }
338 }
339 }
340 return sSessionManager;
341 }
342
Hall Liue362e502016-01-07 17:35:54 -0800343 private static MessageDigest sMessageDigest;
344
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700345 public static void initMd5Sum() {
Hall Liue362e502016-01-07 17:35:54 -0800346 new AsyncTask<Void, Void, Void>() {
347 @Override
348 public Void doInBackground(Void... args) {
349 MessageDigest md;
350 try {
351 md = MessageDigest.getInstance("SHA-1");
352 } catch (NoSuchAlgorithmException e) {
353 md = null;
354 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700355 sMessageDigest = md;
Hall Liue362e502016-01-07 17:35:54 -0800356 return null;
357 }
358 }.execute();
359 }
360
Brad Ebinger51b98342016-09-22 16:30:46 -0700361 public static void setTag(String tag) {
362 TAG = tag;
Brad Ebinger0c3541b2016-11-01 14:11:38 -0700363 DEBUG = isLoggable(android.util.Log.DEBUG);
364 INFO = isLoggable(android.util.Log.INFO);
365 VERBOSE = isLoggable(android.util.Log.VERBOSE);
366 WARN = isLoggable(android.util.Log.WARN);
367 ERROR = isLoggable(android.util.Log.ERROR);
Brad Ebinger51b98342016-09-22 16:30:46 -0700368 }
369
370 /**
371 * If user enabled extended logging is enabled and the time limit has passed, disables the
372 * extended logging.
373 */
374 private static void maybeDisableLogging() {
375 if (!sIsUserExtendedLoggingEnabled) {
376 return;
377 }
378
379 if (sUserExtendedLoggingStopTime < System.currentTimeMillis()) {
380 sUserExtendedLoggingStopTime = 0;
381 sIsUserExtendedLoggingEnabled = false;
382 }
383 }
384
Ihab Awad60ac30b2014-05-20 22:32:12 -0700385 public static boolean isLoggable(int level) {
386 return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
387 }
388
Brad Ebinger51b98342016-09-22 16:30:46 -0700389 public static String piiHandle(Object pii) {
390 if (pii == null || VERBOSE) {
391 return String.valueOf(pii);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700392 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700393
Brad Ebinger51b98342016-09-22 16:30:46 -0700394 StringBuilder sb = new StringBuilder();
395 if (pii instanceof Uri) {
396 Uri uri = (Uri) pii;
397 String scheme = uri.getScheme();
398
399 if (!TextUtils.isEmpty(scheme)) {
400 sb.append(scheme).append(":");
401 }
402
403 String textToObfuscate = uri.getSchemeSpecificPart();
404 if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
405 for (int i = 0; i < textToObfuscate.length(); i++) {
406 char c = textToObfuscate.charAt(i);
407 sb.append(PhoneNumberUtils.isDialable(c) ? "*" : c);
408 }
409 } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
410 for (int i = 0; i < textToObfuscate.length(); i++) {
411 char c = textToObfuscate.charAt(i);
412 if (c != '@' && c != '.') {
413 c = '*';
414 }
415 sb.append(c);
416 }
417 } else {
418 sb.append(pii(pii));
419 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700420 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700421
Brad Ebinger51b98342016-09-22 16:30:46 -0700422 return sb.toString();
Ihab Awad60ac30b2014-05-20 22:32:12 -0700423 }
424
425 /**
426 * Redact personally identifiable information for production users.
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +0900427 * If we are running in verbose mode, return the original string,
428 * and return "****" if we are running on the user build, otherwise
Ihab Awad60ac30b2014-05-20 22:32:12 -0700429 * return a SHA-1 hash of the input string.
430 */
431 public static String pii(Object pii) {
432 if (pii == null || VERBOSE) {
433 return String.valueOf(pii);
434 }
435 return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
436 }
437
438 private static String secureHash(byte[] input) {
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +0900439 // Refrain from logging user personal information in user build.
440 if (USER_BUILD) {
441 return "****";
442 }
443
Brad Ebinger51b98342016-09-22 16:30:46 -0700444 if (sMessageDigest != null) {
445 sMessageDigest.reset();
446 sMessageDigest.update(input);
447 byte[] result = sMessageDigest.digest();
448 return encodeHex(result);
449 } else {
450 return "Uninitialized SHA1";
Ihab Awad60ac30b2014-05-20 22:32:12 -0700451 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700452 }
453
454 private static String encodeHex(byte[] bytes) {
455 StringBuffer hex = new StringBuffer(bytes.length * 2);
456
457 for (int i = 0; i < bytes.length; i++) {
458 int byteIntValue = bytes[i] & 0xff;
459 if (byteIntValue < 0x10) {
460 hex.append("0");
461 }
462 hex.append(Integer.toString(byteIntValue, 16));
463 }
464
465 return hex.toString();
466 }
467
468 private static String getPrefixFromObject(Object obj) {
469 return obj == null ? "<null>" : obj.getClass().getSimpleName();
470 }
471
472 private static String buildMessage(String prefix, String format, Object... args) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700473 // Incorporate thread ID and calling method into prefix
474 String sessionName = getSessionId();
475 String sessionPostfix = TextUtils.isEmpty(sessionName) ? "" : ": " + sessionName;
476
Ihab Awad60ac30b2014-05-20 22:32:12 -0700477 String msg;
478 try {
479 msg = (args == null || args.length == 0) ? format
480 : String.format(Locale.US, format, args);
481 } catch (IllegalFormatException ife) {
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700482 e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
Ihab Awad60ac30b2014-05-20 22:32:12 -0700483 args.length);
484 msg = format + " (An error occurred while formatting the message.)";
485 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700486 return String.format(Locale.US, "%s: %s%s", prefix, msg, sessionPostfix);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700487 }
488}