blob: 4f6a9d6450f87ba13a48aafaabba1c282b7e944d [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
Artur Satayeva5264902019-12-10 17:47:56 +000019import android.compat.annotation.UnsupportedAppUsage;
Brad Ebinger4fb372f2016-10-05 15:47:28 -070020import android.content.Context;
Santos Cordon3c20d632016-02-25 16:12:35 -080021import android.net.Uri;
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.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
Tyler Gunn9bc35112018-04-23 09:52:25 -070047 /**
48 * When generating a bug report, include the last X dialable digits when logging phone numbers.
49 */
50 private static final int NUM_DIALABLE_DIGITS_TO_LOG = Build.IS_USER ? 0 : 2;
51
Brad Ebinger51b98342016-09-22 16:30:46 -070052 // Generic tag for all Telecom logging
53 @VisibleForTesting
54 public static String TAG = "TelecomFramework";
Brad Ebinger0c3541b2016-11-01 14:11:38 -070055 public static boolean DEBUG = isLoggable(android.util.Log.DEBUG);
56 public static boolean INFO = isLoggable(android.util.Log.INFO);
57 public static boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
58 public static boolean WARN = isLoggable(android.util.Log.WARN);
59 public static boolean ERROR = isLoggable(android.util.Log.ERROR);
Brad Ebinger51b98342016-09-22 16:30:46 -070060
61 private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
Jeff Sharkey5ab02432017-06-27 11:01:36 -060062 private static final boolean USER_BUILD = Build.IS_USER;
Ihab Awad60ac30b2014-05-20 22:32:12 -070063
Brad Ebinger51b98342016-09-22 16:30:46 -070064 // Used to synchronize singleton logging lazy initialization
65 private static final Object sSingletonSync = new Object();
66 private static EventManager sEventManager;
67 private static SessionManager sSessionManager;
68
69 /**
70 * Tracks whether user-activated extended logging is enabled.
71 */
72 private static boolean sIsUserExtendedLoggingEnabled = false;
73
74 /**
75 * The time when user-activated extended logging should be ended. Used to determine when
76 * extended logging should automatically be disabled.
77 */
78 private static long sUserExtendedLoggingStopTime = 0;
79
80 private Log() {
81 }
82
83 public static void d(String prefix, String format, Object... args) {
84 if (sIsUserExtendedLoggingEnabled) {
85 maybeDisableLogging();
86 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
87 } else if (DEBUG) {
88 android.util.Slog.d(TAG, buildMessage(prefix, format, args));
89 }
90 }
91
92 public static void d(Object objectPrefix, String format, Object... args) {
93 if (sIsUserExtendedLoggingEnabled) {
94 maybeDisableLogging();
95 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
96 } else if (DEBUG) {
97 android.util.Slog.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
98 }
99 }
100
Andrei Oneafbc8cfd2019-03-22 11:32:59 +0000101 @UnsupportedAppUsage
Brad Ebinger51b98342016-09-22 16:30:46 -0700102 public static void i(String prefix, String format, Object... args) {
103 if (INFO) {
104 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
105 }
106 }
107
108 public static void i(Object objectPrefix, String format, Object... args) {
109 if (INFO) {
110 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
111 }
112 }
113
114 public static void v(String prefix, String format, Object... args) {
115 if (sIsUserExtendedLoggingEnabled) {
116 maybeDisableLogging();
117 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
118 } else if (VERBOSE) {
119 android.util.Slog.v(TAG, buildMessage(prefix, format, args));
120 }
121 }
122
123 public static void v(Object objectPrefix, String format, Object... args) {
124 if (sIsUserExtendedLoggingEnabled) {
125 maybeDisableLogging();
126 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
127 } else if (VERBOSE) {
128 android.util.Slog.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
129 }
130 }
131
Andrei Oneafbc8cfd2019-03-22 11:32:59 +0000132 @UnsupportedAppUsage
Brad Ebinger51b98342016-09-22 16:30:46 -0700133 public static void w(String prefix, String format, Object... args) {
134 if (WARN) {
135 android.util.Slog.w(TAG, buildMessage(prefix, format, args));
136 }
137 }
138
139 public static void w(Object objectPrefix, String format, Object... args) {
140 if (WARN) {
141 android.util.Slog.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
142 }
143 }
144
145 public static void e(String prefix, Throwable tr, String format, Object... args) {
146 if (ERROR) {
147 android.util.Slog.e(TAG, buildMessage(prefix, format, args), tr);
148 }
149 }
150
151 public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
152 if (ERROR) {
153 android.util.Slog.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
154 tr);
155 }
156 }
157
158 public static void wtf(String prefix, Throwable tr, String format, Object... args) {
159 android.util.Slog.wtf(TAG, buildMessage(prefix, format, args), tr);
160 }
161
162 public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
163 android.util.Slog.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
164 tr);
165 }
166
167 public static void wtf(String prefix, String format, Object... args) {
168 String msg = buildMessage(prefix, format, args);
169 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
170 }
171
172 public static void wtf(Object objectPrefix, String format, Object... args) {
173 String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
174 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
175 }
176
177 /**
178 * The ease of use methods below only act mostly as proxies to the Session and Event Loggers.
179 * They also control the lazy loaders of the singleton instances, which will never be loaded if
180 * the proxy methods aren't used.
181 *
182 * Please see each method's documentation inside of their respective implementations in the
183 * loggers.
184 */
185
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700186 public static void setSessionContext(Context context) {
187 getSessionManager().setContext(context);
188 }
189
Brad Ebinger51b98342016-09-22 16:30:46 -0700190 public static void startSession(String shortMethodName) {
191 getSessionManager().startSession(shortMethodName, null);
192 }
193
Brad Ebinger3445f822016-10-24 16:40:49 -0700194 public static void startSession(Session.Info info, String shortMethodName) {
195 getSessionManager().startSession(info, shortMethodName, null);
196 }
197
Brad Ebinger51b98342016-09-22 16:30:46 -0700198 public static void startSession(String shortMethodName, String callerIdentification) {
199 getSessionManager().startSession(shortMethodName, callerIdentification);
200 }
201
Brad Ebingera0dc9762016-10-21 09:41:29 -0700202 public static void startSession(Session.Info info, String shortMethodName,
203 String callerIdentification) {
204 getSessionManager().startSession(info, shortMethodName, callerIdentification);
205 }
206
Brad Ebinger51b98342016-09-22 16:30:46 -0700207 public static Session createSubsession() {
208 return getSessionManager().createSubsession();
209 }
210
Brad Ebinger3445f822016-10-24 16:40:49 -0700211 public static Session.Info getExternalSession() {
212 return getSessionManager().getExternalSession();
213 }
214
Brad Ebinger51b98342016-09-22 16:30:46 -0700215 public static void cancelSubsession(Session subsession) {
216 getSessionManager().cancelSubsession(subsession);
217 }
218
219 public static void continueSession(Session subsession, String shortMethodName) {
220 getSessionManager().continueSession(subsession, shortMethodName);
221 }
222
223 public static void endSession() {
224 getSessionManager().endSession();
225 }
226
Brad Ebinger836efad2016-10-18 13:48:17 -0700227 public static void registerSessionListener(SessionManager.ISessionListener l) {
228 getSessionManager().registerSessionListener(l);
229 }
230
Brad Ebinger51b98342016-09-22 16:30:46 -0700231 public static String getSessionId() {
232 // If the Session logger has not been initialized, then there have been no sessions logged.
233 // Don't load it now!
234 synchronized (sSingletonSync) {
235 if (sSessionManager != null) {
236 return getSessionManager().getSessionId();
237 } else {
238 return "";
239 }
240 }
241 }
242
243 public static void addEvent(EventManager.Loggable recordEntry, String event) {
244 getEventManager().event(recordEntry, event, null);
245 }
246
247 public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
248 getEventManager().event(recordEntry, event, data);
249 }
250
251 public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
252 Object... args) {
253 getEventManager().event(recordEntry, event, format, args);
254 }
255
256 public static void registerEventListener(EventManager.EventListener e) {
257 getEventManager().registerEventListener(e);
258 }
259
260 public static void addRequestResponsePair(EventManager.TimedEventPair p) {
261 getEventManager().addRequestResponsePair(p);
262 }
263
264 public static void dumpEvents(IndentingPrintWriter pw) {
265 // If the Events logger has not been initialized, then there have been no events logged.
266 // Don't load it now!
267 synchronized (sSingletonSync) {
268 if (sEventManager != null) {
269 getEventManager().dumpEvents(pw);
270 } else {
271 pw.println("No Historical Events Logged.");
272 }
273 }
274 }
275
276 /**
Tyler Gunn2db81b52017-05-19 10:10:23 -0700277 * Dumps the events in a timeline format.
278 * @param pw The {@link IndentingPrintWriter} to write to.
279 * @hide
280 */
281 public static void dumpEventsTimeline(IndentingPrintWriter pw) {
282 // If the Events logger has not been initialized, then there have been no events logged.
283 // Don't load it now!
284 synchronized (sSingletonSync) {
285 if (sEventManager != null) {
286 getEventManager().dumpEventsTimeline(pw);
287 } else {
288 pw.println("No Historical Events Logged.");
289 }
290 }
291 }
292
293 /**
Brad Ebinger51b98342016-09-22 16:30:46 -0700294 * Enable or disable extended telecom logging.
295 *
296 * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
297 * {@code false} if it should be disabled.
298 */
299 public static void setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled) {
300 // If the state hasn't changed, bail early.
301 if (sIsUserExtendedLoggingEnabled == isExtendedLoggingEnabled) {
302 return;
303 }
304
305 if (sEventManager != null) {
306 sEventManager.changeEventCacheSize(isExtendedLoggingEnabled ?
307 EVENTS_TO_CACHE_DEBUG : EVENTS_TO_CACHE);
308 }
309
310 sIsUserExtendedLoggingEnabled = isExtendedLoggingEnabled;
311 if (sIsUserExtendedLoggingEnabled) {
312 sUserExtendedLoggingStopTime = System.currentTimeMillis()
313 + EXTENDED_LOGGING_DURATION_MILLIS;
314 } else {
315 sUserExtendedLoggingStopTime = 0;
316 }
317 }
318
319 private static EventManager getEventManager() {
320 // Checking for null again outside of synchronization because we only need to synchronize
321 // during the lazy loading of the events logger. We don't need to synchronize elsewhere.
322 if (sEventManager == null) {
323 synchronized (sSingletonSync) {
324 if (sEventManager == null) {
325 sEventManager = new EventManager(Log::getSessionId);
326 return sEventManager;
327 }
328 }
329 }
330 return sEventManager;
331 }
332
Brad Ebinger8adafe72017-06-08 15:44:40 -0700333 @VisibleForTesting
334 public static SessionManager getSessionManager() {
Brad Ebinger51b98342016-09-22 16:30:46 -0700335 // Checking for null again outside of synchronization because we only need to synchronize
336 // during the lazy loading of the session logger. We don't need to synchronize elsewhere.
337 if (sSessionManager == null) {
338 synchronized (sSingletonSync) {
339 if (sSessionManager == null) {
340 sSessionManager = new SessionManager();
341 return sSessionManager;
342 }
343 }
344 }
345 return sSessionManager;
346 }
347
Brad Ebinger51b98342016-09-22 16:30:46 -0700348 public static void setTag(String tag) {
349 TAG = tag;
Brad Ebinger0c3541b2016-11-01 14:11:38 -0700350 DEBUG = isLoggable(android.util.Log.DEBUG);
351 INFO = isLoggable(android.util.Log.INFO);
352 VERBOSE = isLoggable(android.util.Log.VERBOSE);
353 WARN = isLoggable(android.util.Log.WARN);
354 ERROR = isLoggable(android.util.Log.ERROR);
Brad Ebinger51b98342016-09-22 16:30:46 -0700355 }
356
357 /**
358 * If user enabled extended logging is enabled and the time limit has passed, disables the
359 * extended logging.
360 */
361 private static void maybeDisableLogging() {
362 if (!sIsUserExtendedLoggingEnabled) {
363 return;
364 }
365
366 if (sUserExtendedLoggingStopTime < System.currentTimeMillis()) {
367 sUserExtendedLoggingStopTime = 0;
368 sIsUserExtendedLoggingEnabled = false;
369 }
370 }
371
Ihab Awad60ac30b2014-05-20 22:32:12 -0700372 public static boolean isLoggable(int level) {
373 return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
374 }
375
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800376 /**
377 * Generates an obfuscated string for a calling handle in {@link Uri} format, or a raw phone
378 * phone number in {@link String} format.
379 * @param pii The information to obfuscate.
380 * @return The obfuscated string.
381 */
Brad Ebinger51b98342016-09-22 16:30:46 -0700382 public static String piiHandle(Object pii) {
383 if (pii == null || VERBOSE) {
384 return String.valueOf(pii);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700385 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700386
Brad Ebinger51b98342016-09-22 16:30:46 -0700387 StringBuilder sb = new StringBuilder();
388 if (pii instanceof Uri) {
389 Uri uri = (Uri) pii;
390 String scheme = uri.getScheme();
391
392 if (!TextUtils.isEmpty(scheme)) {
393 sb.append(scheme).append(":");
394 }
395
396 String textToObfuscate = uri.getSchemeSpecificPart();
397 if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800398 obfuscatePhoneNumber(sb, textToObfuscate);
Brad Ebinger51b98342016-09-22 16:30:46 -0700399 } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
400 for (int i = 0; i < textToObfuscate.length(); i++) {
401 char c = textToObfuscate.charAt(i);
402 if (c != '@' && c != '.') {
403 c = '*';
404 }
405 sb.append(c);
406 }
407 } else {
408 sb.append(pii(pii));
409 }
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800410 } else if (pii instanceof String) {
411 String number = (String) pii;
412 obfuscatePhoneNumber(sb, number);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700413 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700414
Brad Ebinger51b98342016-09-22 16:30:46 -0700415 return sb.toString();
Ihab Awad60ac30b2014-05-20 22:32:12 -0700416 }
417
418 /**
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800419 * Obfuscates a phone number, allowing NUM_DIALABLE_DIGITS_TO_LOG digits to be exposed for the
420 * phone number.
421 * @param sb String buffer to write obfuscated number to.
422 * @param phoneNumber The number to obfuscate.
423 */
424 private static void obfuscatePhoneNumber(StringBuilder sb, String phoneNumber) {
425 int numDigitsToObfuscate = getDialableCount(phoneNumber)
426 - NUM_DIALABLE_DIGITS_TO_LOG;
427 for (int i = 0; i < phoneNumber.length(); i++) {
428 char c = phoneNumber.charAt(i);
429 boolean isDialable = PhoneNumberUtils.isDialable(c);
430 if (isDialable) {
431 numDigitsToObfuscate--;
432 }
433 sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c);
434 }
435 }
436
437 /**
Tyler Gunn9bc35112018-04-23 09:52:25 -0700438 * Determines the number of dialable characters in a string.
439 * @param toCount The string to count dialable characters in.
440 * @return The count of dialable characters.
441 */
442 private static int getDialableCount(String toCount) {
443 int numDialable = 0;
444 for (char c : toCount.toCharArray()) {
445 if (PhoneNumberUtils.isDialable(c)) {
446 numDialable++;
447 }
448 }
449 return numDialable;
450 }
451
452 /**
Ihab Awad60ac30b2014-05-20 22:32:12 -0700453 * Redact personally identifiable information for production users.
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +0900454 * If we are running in verbose mode, return the original string,
Brad Ebingerf784b292017-12-22 13:45:27 -0800455 * and return "***" otherwise.
Ihab Awad60ac30b2014-05-20 22:32:12 -0700456 */
457 public static String pii(Object pii) {
458 if (pii == null || VERBOSE) {
459 return String.valueOf(pii);
460 }
Brad Ebingerf784b292017-12-22 13:45:27 -0800461 return "***";
Ihab Awad60ac30b2014-05-20 22:32:12 -0700462 }
463
464 private static String getPrefixFromObject(Object obj) {
465 return obj == null ? "<null>" : obj.getClass().getSimpleName();
466 }
467
468 private static String buildMessage(String prefix, String format, Object... args) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700469 // Incorporate thread ID and calling method into prefix
470 String sessionName = getSessionId();
471 String sessionPostfix = TextUtils.isEmpty(sessionName) ? "" : ": " + sessionName;
472
Ihab Awad60ac30b2014-05-20 22:32:12 -0700473 String msg;
474 try {
475 msg = (args == null || args.length == 0) ? format
476 : String.format(Locale.US, format, args);
477 } catch (IllegalFormatException ife) {
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700478 e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
Ihab Awad60ac30b2014-05-20 22:32:12 -0700479 args.length);
480 msg = format + " (An error occurred while formatting the message.)";
481 }
Brad Ebinger51b98342016-09-22 16:30:46 -0700482 return String.format(Locale.US, "%s: %s%s", prefix, msg, sessionPostfix);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700483 }
484}