• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.display.plugin;
18 
19 import android.util.IndentingPrintWriter;
20 
21 import com.android.internal.util.RingBuffer;
22 
23 import java.io.PrintWriter;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 
33 class PluginEventStorage {
34     private static final long TIME_FRAME_LENGTH = 60_000;
35     private static final long MIN_EVENT_DELAY = 500;
36     private static final int MAX_TIME_FRAMES = 10;
37     // not thread safe
38     private static final SimpleDateFormat sDateFormat = new SimpleDateFormat(
39             "MM-dd HH:mm:ss.SSS", Locale.US);
40 
41     RingBuffer<TimeFrame> mEvents = new RingBuffer<>(
42             TimeFrame::new, TimeFrame[]::new, MAX_TIME_FRAMES);
43 
44     private final Map<PluginType<?>, Long> mEventTimes = new HashMap<>();
45     private long mTimeFrameStart = 0;
46     private final Map<PluginType<?>, EventCounter> mCounters = new HashMap<>();
47 
onValueUpdated(PluginType<T> type)48     <T> void onValueUpdated(PluginType<T> type) {
49         long eventTime = System.currentTimeMillis();
50         if (eventTime - TIME_FRAME_LENGTH > mTimeFrameStart) { // event is in next TimeFrame
51             closeCurrentTimeFrame();
52             mTimeFrameStart = eventTime;
53         }
54         updateCurrentTimeFrame(type, eventTime);
55     }
56 
closeCurrentTimeFrame()57     private void closeCurrentTimeFrame() {
58         if (!mCounters.isEmpty()) {
59             mEvents.append(new TimeFrame(
60                     mTimeFrameStart, mTimeFrameStart + TIME_FRAME_LENGTH, mCounters));
61             mCounters.clear();
62         }
63     }
64 
updateCurrentTimeFrame(PluginType<T> type, long eventTime)65     private <T> void updateCurrentTimeFrame(PluginType<T> type, long eventTime) {
66         EventCounter counter = mCounters.get(type);
67         long previousTimestamp = mEventTimes.getOrDefault(type, 0L);
68         if (counter == null) {
69             counter = new EventCounter();
70             mCounters.put(type, counter);
71         }
72         counter.increase(eventTime, previousTimestamp);
73         mEventTimes.put(type, eventTime);
74     }
75 
getTimeFrames()76     List<TimeFrame> getTimeFrames() {
77         List<TimeFrame> timeFrames = new ArrayList<>(Arrays.stream(mEvents.toArray()).toList());
78         timeFrames.add(new TimeFrame(
79                 mTimeFrameStart, System.currentTimeMillis(), mCounters));
80         return timeFrames;
81     }
82 
83     static class TimeFrame {
84         private final long mStart;
85         private final long mEnd;
86         private final  Map<PluginType<?>, EventCounter> mCounters;
87 
TimeFrame()88         private TimeFrame() {
89             this(0, 0, Map.of());
90         }
91 
TimeFrame(long start, long end, Map<PluginType<?>, EventCounter> counters)92         private TimeFrame(long start, long end, Map<PluginType<?>, EventCounter> counters) {
93             mStart = start;
94             mEnd = end;
95             mCounters = new HashMap<>(counters);
96         }
97 
98         @SuppressWarnings("JavaUtilDate")
dump(PrintWriter pw)99         void dump(PrintWriter pw) {
100             pw.append("TimeFrame:[")
101                     .append(sDateFormat.format(new Date(mStart)))
102                     .append(" - ")
103                     .append(sDateFormat.format(new Date(mEnd)))
104                     .println("]:");
105             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
106             if (mCounters.isEmpty()) {
107                 ipw.println("NO EVENTS");
108             } else {
109                 for (Map.Entry<PluginType<?>, EventCounter> entry: mCounters.entrySet()) {
110                     ipw.append(entry.getKey().mName).append(" -> {");
111                     entry.getValue().dump(ipw);
112                     ipw.println("}");
113                 }
114             }
115         }
116     }
117 
118     private static class EventCounter {
119         private int mEventCounter = 0;
120         private int mFastEventCounter = 0;
121 
increase(long timestamp, long previousTimestamp)122         private void increase(long timestamp, long previousTimestamp) {
123             mEventCounter++;
124             if (timestamp - previousTimestamp < MIN_EVENT_DELAY) {
125                 mFastEventCounter++;
126             }
127         }
128 
dump(PrintWriter pw)129         private void dump(PrintWriter pw) {
130             pw.append("Count:").append(String.valueOf(mEventCounter))
131                     .append("; Fast:").append(String.valueOf(mFastEventCounter));
132         }
133     }
134 }
135