summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/hdmi/HdmiLogger.java
blob: 2562ffc7898aa167662598da1474db07e9f1ab4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.hdmi;

import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.Pair;
import android.util.Slog;

import java.util.HashMap;

/**
 * A logger that prevents spammy log. For the same log message, it logs once every 20seconds.
 * This class is not thread-safe.
 * <p>
 * For convenience, use single character prefix for all messages.
 * Here are common acronyms
 * <ul>
 *   <li>[T]: Timout
 *   <li>[R]: Received message
 *   <li>[S]: Sent message
 *   <li>[P]: Device polling result
 * </ul>
 */
final class HdmiLogger {
    private static final String TAG = "HDMI";
    // Logging duration for same error message.
    private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000;  // 20s

    private static final boolean DEBUG = false;

    private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>();

    // Key (String): log message.
    // Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage.
    // Cache for warning.
    private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>();
    // Cache for error.
    private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>();

    private HdmiLogger() {
    }

    static final void warning(String logMessage, Object... objs) {
        getLogger().warningInternal(toLogString(logMessage, objs));
    }

    private void warningInternal(String logMessage) {
        String log = updateLog(mWarningTimingCache, logMessage);
        if (!log.isEmpty()) {
            Slog.w(TAG, log);
        }
    }

    static final void error(String logMessage, Object... objs) {
        getLogger().errorInternal(toLogString(logMessage, objs));
    }

    private void errorInternal(String logMessage) {
        String log = updateLog(mErrorTimingCache, logMessage);
        if (!log.isEmpty()) {
            Slog.e(TAG, log);
        }
    }

    static final void debug(String logMessage, Object... objs) {
        getLogger().debugInternal(toLogString(logMessage, objs));
    }

    private void debugInternal(String logMessage) {
        if (!DEBUG) {
            return;
        }
        Slog.d(TAG, logMessage);
    }

    private static final String toLogString(String logMessage, Object[] objs) {
        if (objs.length > 0) {
            return String.format(logMessage, objs);
        } else {
            return logMessage;
        }
    }

    private static HdmiLogger getLogger() {
        HdmiLogger logger = sLogger.get();
        if (logger == null) {
            logger = new HdmiLogger();
            sLogger.set(logger);
        }
        return logger;
    }

    private static String updateLog(HashMap<String, Pair<Long, Integer>> cache, String logMessage) {
        long curTime = SystemClock.uptimeMillis();
        Pair<Long, Integer> timing = cache.get(logMessage);
        if (shouldLogNow(timing, curTime)) {
            String log = buildMessage(logMessage, timing);
            cache.put(logMessage, new Pair<>(curTime, 1));
            return log;
        } else {
            increaseLogCount(cache, logMessage);
        }
        return "";
    }

    private static String buildMessage(String message, @Nullable Pair<Long, Integer> timing) {
        return new StringBuilder()
                .append("[").append(timing == null ? 1 : timing.second).append("]:")
                .append(message).toString();
    }

    private static void increaseLogCount(HashMap<String, Pair<Long, Integer>> cache,
            String message) {
        Pair<Long, Integer> timing = cache.get(message);
        if (timing != null) {
            cache.put(message, new Pair<>(timing.first, timing.second + 1));
        }
    }

    private static boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) {
        return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS;
    }
}