blob: 1e5769fad1df2611f23ed57de8c49acebcf640f0 [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";
51
52 private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
Ihab Awad60ac30b2014-05-20 22:32:12 -070053 public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
54 public static final boolean INFO = isLoggable(android.util.Log.INFO);
55 public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
56 public static final boolean WARN = isLoggable(android.util.Log.WARN);
57 public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
58
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
187 public static void startSession(String shortMethodName, String callerIdentification) {
188 getSessionManager().startSession(shortMethodName, callerIdentification);
189 }
190
Brad Ebingera0dc9762016-10-21 09:41:29 -0700191 public static void startSession(Session.Info info, String shortMethodName,
192 String callerIdentification) {
193 getSessionManager().startSession(info, shortMethodName, callerIdentification);
194 }
195
196 public static void startExternalSession(Session.Info sessionInfo, String shortMethodName) {
197 getSessionManager().startExternalSession(sessionInfo, shortMethodName);
198 }
199
Brad Ebinger51b98342016-09-22 16:30:46 -0700200 public static Session createSubsession() {
201 return getSessionManager().createSubsession();
202 }
203
204 public static void cancelSubsession(Session subsession) {
205 getSessionManager().cancelSubsession(subsession);
206 }
207
208 public static void continueSession(Session subsession, String shortMethodName) {
209 getSessionManager().continueSession(subsession, shortMethodName);
210 }
211
212 public static void endSession() {
213 getSessionManager().endSession();
214 }
215
Brad Ebinger836efad2016-10-18 13:48:17 -0700216 public static void registerSessionListener(SessionManager.ISessionListener l) {
217 getSessionManager().registerSessionListener(l);
218 }
219
Brad Ebinger51b98342016-09-22 16:30:46 -0700220 public static String getSessionId() {
221 // If the Session logger has not been initialized, then there have been no sessions logged.
222 // Don't load it now!
223 synchronized (sSingletonSync) {
224 if (sSessionManager != null) {
225 return getSessionManager().getSessionId();
226 } else {
227 return "";
228 }
229 }
230 }
231
232 public static void addEvent(EventManager.Loggable recordEntry, String event) {
233 getEventManager().event(recordEntry, event, null);
234 }
235
236 public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
237 getEventManager().event(recordEntry, event, data);
238 }
239
240 public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
241 Object... args) {
242 getEventManager().event(recordEntry, event, format, args);
243 }
244
245 public static void registerEventListener(EventManager.EventListener e) {
246 getEventManager().registerEventListener(e);
247 }
248
249 public static void addRequestResponsePair(EventManager.TimedEventPair p) {
250 getEventManager().addRequestResponsePair(p);
251 }
252
253 public static void dumpEvents(IndentingPrintWriter pw) {
254 // If the Events logger has not been initialized, then there have been no events logged.
255 // Don't load it now!
256 synchronized (sSingletonSync) {
257 if (sEventManager != null) {
258 getEventManager().dumpEvents(pw);
259 } else {
260 pw.println("No Historical Events Logged.");
261 }
262 }
263 }
264
265 /**
266 * Enable or disable extended telecom logging.
267 *
268 * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
269 * {@code false} if it should be disabled.
270 */
271 public static void setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled) {
272 // If the state hasn't changed, bail early.
273 if (sIsUserExtendedLoggingEnabled == isExtendedLoggingEnabled) {
274 return;
275 }
276
277 if (sEventManager != null) {
278 sEventManager.changeEventCacheSize(isExtendedLoggingEnabled ?
279 EVENTS_TO_CACHE_DEBUG : EVENTS_TO_CACHE);
280 }
281
282 sIsUserExtendedLoggingEnabled = isExtendedLoggingEnabled;
283 if (sIsUserExtendedLoggingEnabled) {
284 sUserExtendedLoggingStopTime = System.currentTimeMillis()
285 + EXTENDED_LOGGING_DURATION_MILLIS;
286 } else {
287 sUserExtendedLoggingStopTime = 0;
288 }
289 }
290
291 private static EventManager getEventManager() {
292 // Checking for null again outside of synchronization because we only need to synchronize
293 // during the lazy loading of the events logger. We don't need to synchronize elsewhere.
294 if (sEventManager == null) {
295 synchronized (sSingletonSync) {
296 if (sEventManager == null) {
297 sEventManager = new EventManager(Log::getSessionId);
298 return sEventManager;
299 }
300 }
301 }
302 return sEventManager;
303 }
304
305 private static SessionManager getSessionManager() {
306 // Checking for null again outside of synchronization because we only need to synchronize
307 // during the lazy loading of the session logger. We don't need to synchronize elsewhere.
308 if (sSessionManager == null) {
309 synchronized (sSingletonSync) {
310 if (sSessionManager == null) {
311 sSessionManager = new SessionManager();
312 return sSessionManager;
313 }
314 }
315 }
316 return sSessionManager;
317 }
318
Hall Liue362e502016-01-07 17:35:54 -0800319 private static MessageDigest sMessageDigest;
320
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700321 public static void initMd5Sum() {
Hall Liue362e502016-01-07 17:35:54 -0800322 new AsyncTask<Void, Void, Void>() {
323 @Override
324 public Void doInBackground(Void... args) {
325 MessageDigest md;
326 try {
327 md = MessageDigest.getInstance("SHA-1");
328 } catch (NoSuchAlgorithmException e) {
329 md = null;
330 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700331 sMessageDigest = md;
Hall Liue362e502016-01-07 17:35:54 -0800332 return null;
333 }
334 }.execute();
335 }
336
Brad Ebinger51b98342016-09-22 16:30:46 -0700337 public static void setTag(String tag) {
338 TAG = tag;
339 }
340
341 /**
342 * If user enabled extended logging is enabled and the time limit has passed, disables the
343 * extended logging.
344 */
345 private static void maybeDisableLogging() {
346 if (!sIsUserExtendedLoggingEnabled) {
347 return;
348 }
349
350 if (sUserExtendedLoggingStopTime < System.currentTimeMillis()) {
351 sUserExtendedLoggingStopTime = 0;
352 sIsUserExtendedLoggingEnabled = false;
353 }
354 }
355
Ihab Awad60ac30b2014-05-20 22:32:12 -0700356 public static boolean isLoggable(int level) {
357 return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
358 }
359
Brad Ebinger51b98342016-09-22 16:30:46 -0700360 public static String piiHandle(Object pii) {
361 if (pii == null || VERBOSE) {
362 return String.valueOf(pii);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700363 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700364
Brad Ebinger51b98342016-09-22 16:30:46 -0700365 StringBuilder sb = new StringBuilder();
366 if (pii instanceof Uri) {
367 Uri uri = (Uri) pii;
368 String scheme = uri.getScheme();
369
370 if (!TextUtils.isEmpty(scheme)) {
371 sb.append(scheme).append(":");
372 }
373
374 String textToObfuscate = uri.getSchemeSpecificPart();
375 if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
376 for (int i = 0; i < textToObfuscate.length(); i++) {
377 char c = textToObfuscate.charAt(i);
378 sb.append(PhoneNumberUtils.isDialable(c) ? "*" : c);
379 }
380 } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
381 for (int i = 0; i < textToObfuscate.length(); i++) {
382 char c = textToObfuscate.charAt(i);
383 if (c != '@' && c != '.') {
384 c = '*';
385 }
386 sb.append(c);
387 }
388 } else {
389 sb.append(pii(pii));
390 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700391 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700392
Brad Ebinger51b98342016-09-22 16:30:46 -0700393 return sb.toString();
Ihab Awad60ac30b2014-05-20 22:32:12 -0700394 }
395
396 /**
397 * Redact personally identifiable information for production users.
398 * If we are running in verbose mode, return the original string, otherwise
399 * return a SHA-1 hash of the input string.
400 */
401 public static String pii(Object pii) {
402 if (pii == null || VERBOSE) {
403 return String.valueOf(pii);
404 }
405 return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
406 }
407
408 private static String secureHash(byte[] input) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700409 if (sMessageDigest != null) {
410 sMessageDigest.reset();
411 sMessageDigest.update(input);
412 byte[] result = sMessageDigest.digest();
413 return encodeHex(result);
414 } else {
415 return "Uninitialized SHA1";
Ihab Awad60ac30b2014-05-20 22:32:12 -0700416 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700417 }
418
419 private static String encodeHex(byte[] bytes) {
420 StringBuffer hex = new StringBuffer(bytes.length * 2);
421
422 for (int i = 0; i < bytes.length; i++) {
423 int byteIntValue = bytes[i] & 0xff;
424 if (byteIntValue < 0x10) {
425 hex.append("0");
426 }
427 hex.append(Integer.toString(byteIntValue, 16));
428 }
429
430 return hex.toString();
431 }
432
433 private static String getPrefixFromObject(Object obj) {
434 return obj == null ? "<null>" : obj.getClass().getSimpleName();
435 }
436
437 private static String buildMessage(String prefix, String format, Object... args) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700438 // Incorporate thread ID and calling method into prefix
439 String sessionName = getSessionId();
440 String sessionPostfix = TextUtils.isEmpty(sessionName) ? "" : ": " + sessionName;
441
Ihab Awad60ac30b2014-05-20 22:32:12 -0700442 String msg;
443 try {
444 msg = (args == null || args.length == 0) ? format
445 : String.format(Locale.US, format, args);
446 } catch (IllegalFormatException ife) {
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700447 e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
Ihab Awad60ac30b2014-05-20 22:32:12 -0700448 args.length);
449 msg = format + " (An error occurred while formatting the message.)";
450 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700451 return String.format(Locale.US, "%s: %s%s", prefix, msg, sessionPostfix);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700452 }
453}