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