Home | History | Annotate | Download | only in hdmi
      1 /*
      2  * Copyright (C) 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 com.android.server.hdmi;
     18 
     19 import android.annotation.Nullable;
     20 import android.os.SystemClock;
     21 import android.util.Pair;
     22 import android.util.Slog;
     23 import android.util.Log;
     24 
     25 import java.util.HashMap;
     26 
     27 /**
     28  * A logger that prevents spammy log. For the same log message, it logs once every 20seconds.
     29  * This class is not thread-safe.
     30  * <p>
     31  * For convenience, use single character prefix for all messages.
     32  * Here are common acronyms
     33  * <ul>
     34  *   <li>[T]: Timout
     35  *   <li>[R]: Received message
     36  *   <li>[S]: Sent message
     37  *   <li>[P]: Device polling result
     38  * </ul>
     39  */
     40 final class HdmiLogger {
     41     private static final String TAG = "HDMI";
     42     // Logging duration for same error message.
     43     private static final long ERROR_LOG_DURATION_MILLIS = 20 * 1000;  // 20s
     44 
     45     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     46 
     47     private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>();
     48 
     49     // Key (String): log message.
     50     // Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage.
     51     // Cache for warning.
     52     private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>();
     53     // Cache for error.
     54     private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>();
     55 
     56     private HdmiLogger() {
     57     }
     58 
     59     static final void warning(String logMessage, Object... objs) {
     60         getLogger().warningInternal(toLogString(logMessage, objs));
     61     }
     62 
     63     private void warningInternal(String logMessage) {
     64         String log = updateLog(mWarningTimingCache, logMessage);
     65         if (!log.isEmpty()) {
     66             Slog.w(TAG, log);
     67         }
     68     }
     69 
     70     static final void error(String logMessage, Object... objs) {
     71         getLogger().errorInternal(toLogString(logMessage, objs));
     72     }
     73 
     74     private void errorInternal(String logMessage) {
     75         String log = updateLog(mErrorTimingCache, logMessage);
     76         if (!log.isEmpty()) {
     77             Slog.e(TAG, log);
     78         }
     79     }
     80 
     81     static final void debug(String logMessage, Object... objs) {
     82         getLogger().debugInternal(toLogString(logMessage, objs));
     83     }
     84 
     85     private void debugInternal(String logMessage) {
     86         if (DEBUG) {
     87             Slog.d(TAG, logMessage);
     88         }
     89     }
     90 
     91     private static final String toLogString(String logMessage, Object[] objs) {
     92         if (objs.length > 0) {
     93             return String.format(logMessage, objs);
     94         } else {
     95             return logMessage;
     96         }
     97     }
     98 
     99     private static HdmiLogger getLogger() {
    100         HdmiLogger logger = sLogger.get();
    101         if (logger == null) {
    102             logger = new HdmiLogger();
    103             sLogger.set(logger);
    104         }
    105         return logger;
    106     }
    107 
    108     private static String updateLog(HashMap<String, Pair<Long, Integer>> cache, String logMessage) {
    109         long curTime = SystemClock.uptimeMillis();
    110         Pair<Long, Integer> timing = cache.get(logMessage);
    111         if (shouldLogNow(timing, curTime)) {
    112             String log = buildMessage(logMessage, timing);
    113             cache.put(logMessage, new Pair<>(curTime, 1));
    114             return log;
    115         } else {
    116             increaseLogCount(cache, logMessage);
    117         }
    118         return "";
    119     }
    120 
    121     private static String buildMessage(String message, @Nullable Pair<Long, Integer> timing) {
    122         return new StringBuilder()
    123                 .append("[").append(timing == null ? 1 : timing.second).append("]:")
    124                 .append(message).toString();
    125     }
    126 
    127     private static void increaseLogCount(HashMap<String, Pair<Long, Integer>> cache,
    128             String message) {
    129         Pair<Long, Integer> timing = cache.get(message);
    130         if (timing != null) {
    131             cache.put(message, new Pair<>(timing.first, timing.second + 1));
    132         }
    133     }
    134 
    135     private static boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) {
    136         return timing == null || curTime - timing.first > ERROR_LOG_DURATION_MILLIS;
    137     }
    138 }
    139