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