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