Home | History | Annotate | Download | only in telecom
      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 
     77     private Log() {
     78     }
     79 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    181     public static void setSessionContext(Context context) {
    182         getSessionManager().setContext(context);
    183     }
    184 
    185     public static void startSession(String shortMethodName) {
    186         getSessionManager().startSession(shortMethodName, null);
    187     }
    188 
    189     public static void startSession(Session.Info info, String shortMethodName) {
    190         getSessionManager().startSession(info, shortMethodName, null);
    191     }
    192 
    193     public static void startSession(String shortMethodName, String callerIdentification) {
    194         getSessionManager().startSession(shortMethodName, callerIdentification);
    195     }
    196 
    197     public static void startSession(Session.Info info, String shortMethodName,
    198             String callerIdentification) {
    199         getSessionManager().startSession(info, shortMethodName, callerIdentification);
    200     }
    201 
    202     public static Session createSubsession() {
    203         return getSessionManager().createSubsession();
    204     }
    205 
    206     public static Session.Info getExternalSession() {
    207         return getSessionManager().getExternalSession();
    208     }
    209 
    210     public static void cancelSubsession(Session subsession) {
    211         getSessionManager().cancelSubsession(subsession);
    212     }
    213 
    214     public static void continueSession(Session subsession, String shortMethodName) {
    215         getSessionManager().continueSession(subsession, shortMethodName);
    216     }
    217 
    218     public static void endSession() {
    219         getSessionManager().endSession();
    220     }
    221 
    222     public static void registerSessionListener(SessionManager.ISessionListener l) {
    223         getSessionManager().registerSessionListener(l);
    224     }
    225 
    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 
    238     public static void addEvent(EventManager.Loggable recordEntry, String event) {
    239         getEventManager().event(recordEntry, event, null);
    240     }
    241 
    242     public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
    243         getEventManager().event(recordEntry, event, data);
    244     }
    245 
    246     public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
    247             Object... args) {
    248         getEventManager().event(recordEntry, event, format, args);
    249     }
    250 
    251     public static void registerEventListener(EventManager.EventListener e) {
    252         getEventManager().registerEventListener(e);
    253     }
    254 
    255     public static void addRequestResponsePair(EventManager.TimedEventPair p) {
    256         getEventManager().addRequestResponsePair(p);
    257     }
    258 
    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      */
    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      */
    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 
    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
    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 
    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      */
    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 
    367     public static boolean isLoggable(int level) {
    368         return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
    369     }
    370 
    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      */
    412     public static String pii(Object pii) {
    413         if (pii == null || VERBOSE) {
    414             return String.valueOf(pii);
    415         }
    416         return "***";
    417     }
    418 
    419     private static String getPrefixFromObject(Object obj) {
    420         return obj == null ? "<null>" : obj.getClass().getSimpleName();
    421     }
    422 
    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