• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.telecom;
18 
19 import android.content.Context;
20 import android.net.Uri;
21 import android.os.AsyncTask;
22 import android.os.Build;
23 import android.telecom.Logging.EventManager;
24 import android.telecom.Logging.Session;
25 import android.telecom.Logging.SessionManager;
26 import android.telephony.PhoneNumberUtils;
27 import android.text.TextUtils;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.internal.util.IndentingPrintWriter;
31 
32 import java.security.MessageDigest;
33 import java.security.NoSuchAlgorithmException;
34 import java.util.IllegalFormatException;
35 import java.util.Locale;
36 
37 /**
38  * Manages logging for the entire module.
39  *
40  * @hide
41  */
42 public class Log {
43 
44     private static final long EXTENDED_LOGGING_DURATION_MILLIS = 60000 * 30; // 30 minutes
45 
46     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";
52     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);
57 
58     private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
59     private static final boolean USER_BUILD = Build.IS_USER;
60 
61     // 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 
Log()77     private Log() {
78     }
79 
d(String prefix, String format, Object... args)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 
d(Object objectPrefix, String format, Object... args)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 
i(String prefix, String format, Object... args)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 
i(Object objectPrefix, String format, Object... args)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 
v(String prefix, String format, Object... args)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 
v(Object objectPrefix, String format, Object... args)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 
w(String prefix, String format, Object... args)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 
w(Object objectPrefix, String format, Object... args)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 
e(String prefix, Throwable tr, String format, Object... args)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 
e(Object objectPrefix, Throwable tr, String format, Object... args)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 
wtf(String prefix, Throwable tr, String format, Object... args)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 
wtf(Object objectPrefix, Throwable tr, String format, Object... args)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 
wtf(String prefix, String format, Object... args)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 
wtf(Object objectPrefix, String format, Object... args)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 
setSessionContext(Context context)181     public static void setSessionContext(Context context) {
182         getSessionManager().setContext(context);
183     }
184 
startSession(String shortMethodName)185     public static void startSession(String shortMethodName) {
186         getSessionManager().startSession(shortMethodName, null);
187     }
188 
startSession(Session.Info info, String shortMethodName)189     public static void startSession(Session.Info info, String shortMethodName) {
190         getSessionManager().startSession(info, shortMethodName, null);
191     }
192 
startSession(String shortMethodName, String callerIdentification)193     public static void startSession(String shortMethodName, String callerIdentification) {
194         getSessionManager().startSession(shortMethodName, callerIdentification);
195     }
196 
startSession(Session.Info info, String shortMethodName, String callerIdentification)197     public static void startSession(Session.Info info, String shortMethodName,
198             String callerIdentification) {
199         getSessionManager().startSession(info, shortMethodName, callerIdentification);
200     }
201 
createSubsession()202     public static Session createSubsession() {
203         return getSessionManager().createSubsession();
204     }
205 
getExternalSession()206     public static Session.Info getExternalSession() {
207         return getSessionManager().getExternalSession();
208     }
209 
cancelSubsession(Session subsession)210     public static void cancelSubsession(Session subsession) {
211         getSessionManager().cancelSubsession(subsession);
212     }
213 
continueSession(Session subsession, String shortMethodName)214     public static void continueSession(Session subsession, String shortMethodName) {
215         getSessionManager().continueSession(subsession, shortMethodName);
216     }
217 
endSession()218     public static void endSession() {
219         getSessionManager().endSession();
220     }
221 
registerSessionListener(SessionManager.ISessionListener l)222     public static void registerSessionListener(SessionManager.ISessionListener l) {
223         getSessionManager().registerSessionListener(l);
224     }
225 
getSessionId()226     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 
addEvent(EventManager.Loggable recordEntry, String event)238     public static void addEvent(EventManager.Loggable recordEntry, String event) {
239         getEventManager().event(recordEntry, event, null);
240     }
241 
addEvent(EventManager.Loggable recordEntry, String event, Object data)242     public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
243         getEventManager().event(recordEntry, event, data);
244     }
245 
addEvent(EventManager.Loggable recordEntry, String event, String format, Object... args)246     public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
247             Object... args) {
248         getEventManager().event(recordEntry, event, format, args);
249     }
250 
registerEventListener(EventManager.EventListener e)251     public static void registerEventListener(EventManager.EventListener e) {
252         getEventManager().registerEventListener(e);
253     }
254 
addRequestResponsePair(EventManager.TimedEventPair p)255     public static void addRequestResponsePair(EventManager.TimedEventPair p) {
256         getEventManager().addRequestResponsePair(p);
257     }
258 
dumpEvents(IndentingPrintWriter pw)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     /**
272      * Dumps the events in a timeline format.
273      * @param pw The {@link IndentingPrintWriter} to write to.
274      * @hide
275      */
dumpEventsTimeline(IndentingPrintWriter pw)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     /**
289      * 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      */
setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled)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 
getEventManager()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 
328     @VisibleForTesting
getSessionManager()329     public static SessionManager getSessionManager() {
330         // 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 
setTag(String tag)343     public static void setTag(String tag) {
344         TAG = tag;
345         DEBUG = isLoggable(android.util.Log.DEBUG);
346         INFO = isLoggable(android.util.Log.INFO);
347         VERBOSE = isLoggable(android.util.Log.VERBOSE);
348         WARN = isLoggable(android.util.Log.WARN);
349         ERROR = isLoggable(android.util.Log.ERROR);
350     }
351 
352     /**
353      * If user enabled extended logging is enabled and the time limit has passed, disables the
354      * extended logging.
355      */
maybeDisableLogging()356     private static void maybeDisableLogging() {
357         if (!sIsUserExtendedLoggingEnabled) {
358             return;
359         }
360 
361         if (sUserExtendedLoggingStopTime < System.currentTimeMillis()) {
362             sUserExtendedLoggingStopTime = 0;
363             sIsUserExtendedLoggingEnabled = false;
364         }
365     }
366 
isLoggable(int level)367     public static boolean isLoggable(int level) {
368         return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
369     }
370 
piiHandle(Object pii)371     public static String piiHandle(Object pii) {
372         if (pii == null || VERBOSE) {
373             return String.valueOf(pii);
374         }
375 
376         StringBuilder sb = new StringBuilder();
377         if (pii instanceof Uri) {
378             Uri uri = (Uri) pii;
379             String scheme = uri.getScheme();
380 
381             if (!TextUtils.isEmpty(scheme)) {
382                 sb.append(scheme).append(":");
383             }
384 
385             String textToObfuscate = uri.getSchemeSpecificPart();
386             if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
387                 for (int i = 0; i < textToObfuscate.length(); i++) {
388                     char c = textToObfuscate.charAt(i);
389                     sb.append(PhoneNumberUtils.isDialable(c) ? "*" : c);
390                 }
391             } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
392                 for (int i = 0; i < textToObfuscate.length(); i++) {
393                     char c = textToObfuscate.charAt(i);
394                     if (c != '@' && c != '.') {
395                         c = '*';
396                     }
397                     sb.append(c);
398                 }
399             } else {
400                 sb.append(pii(pii));
401             }
402         }
403 
404         return sb.toString();
405     }
406 
407     /**
408      * Redact personally identifiable information for production users.
409      * If we are running in verbose mode, return the original string,
410      * and return "***" otherwise.
411      */
pii(Object pii)412     public static String pii(Object pii) {
413         if (pii == null || VERBOSE) {
414             return String.valueOf(pii);
415         }
416         return "***";
417     }
418 
getPrefixFromObject(Object obj)419     private static String getPrefixFromObject(Object obj) {
420         return obj == null ? "<null>" : obj.getClass().getSimpleName();
421     }
422 
buildMessage(String prefix, String format, Object... args)423     private static String buildMessage(String prefix, String format, Object... args) {
424         // Incorporate thread ID and calling method into prefix
425         String sessionName = getSessionId();
426         String sessionPostfix = TextUtils.isEmpty(sessionName) ? "" : ": " + sessionName;
427 
428         String msg;
429         try {
430             msg = (args == null || args.length == 0) ? format
431                     : String.format(Locale.US, format, args);
432         } catch (IllegalFormatException ife) {
433             e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
434                     args.length);
435             msg = format + " (An error occurred while formatting the message.)";
436         }
437         return String.format(Locale.US, "%s: %s%s", prefix, msg, sessionPostfix);
438     }
439 }
440