Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b46a211 authored by petsjonkin's avatar petsjonkin
Browse files

PluginManager: adding event logging

Bug: b/354063547
Test: manual, see https://b.corp.google.com/issues/354063547#comment6
Flag: com.android.server.display.feature.flags.enable_plugin_manager
Change-Id: I53b1288d4b2c2b0824d12a79a0a94e7365363523
parent 66e33150
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.display.plugin;

import android.util.IndentingPrintWriter;

import com.android.internal.util.RingBuffer;

import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

class PluginEventStorage {
    private static final long TIME_FRAME_LENGTH = 60_000;
    private static final long MIN_EVENT_DELAY = 500;
    private static final int MAX_TIME_FRAMES = 10;
    // not thread safe
    private static final SimpleDateFormat sDateFormat = new SimpleDateFormat(
            "MM-dd HH:mm:ss.SSS", Locale.US);

    RingBuffer<TimeFrame> mEvents = new RingBuffer<>(
            TimeFrame::new, TimeFrame[]::new, MAX_TIME_FRAMES);

    private final Map<PluginType<?>, Long> mEventTimes = new HashMap<>();
    private long mTimeFrameStart = 0;
    private final Map<PluginType<?>, EventCounter> mCounters = new HashMap<>();

    <T> void onValueUpdated(PluginType<T> type) {
        long eventTime = System.currentTimeMillis();
        if (eventTime - TIME_FRAME_LENGTH > mTimeFrameStart) { // event is in next TimeFrame
            closeCurrentTimeFrame();
            mTimeFrameStart = eventTime;
        }
        updateCurrentTimeFrame(type, eventTime);
    }

    private void closeCurrentTimeFrame() {
        if (!mCounters.isEmpty()) {
            mEvents.append(new TimeFrame(
                    mTimeFrameStart, mTimeFrameStart + TIME_FRAME_LENGTH, mCounters));
            mCounters.clear();
        }
    }

    private <T> void updateCurrentTimeFrame(PluginType<T> type, long eventTime) {
        EventCounter counter = mCounters.get(type);
        long previousTimestamp = mEventTimes.getOrDefault(type, 0L);
        if (counter == null) {
            counter = new EventCounter();
            mCounters.put(type, counter);
        }
        counter.increase(eventTime, previousTimestamp);
        mEventTimes.put(type, eventTime);
    }

    List<TimeFrame> getTimeFrames() {
        List<TimeFrame> timeFrames = new ArrayList<>(Arrays.stream(mEvents.toArray()).toList());
        timeFrames.add(new TimeFrame(
                mTimeFrameStart, System.currentTimeMillis(), mCounters));
        return timeFrames;
    }

    static class TimeFrame {
        private final long mStart;
        private final long mEnd;
        private final  Map<PluginType<?>, EventCounter> mCounters;

        private TimeFrame() {
            this(0, 0, Map.of());
        }

        private TimeFrame(long start, long end, Map<PluginType<?>, EventCounter> counters) {
            mStart = start;
            mEnd = end;
            mCounters = new HashMap<>(counters);
        }

        @SuppressWarnings("JavaUtilDate")
        void dump(PrintWriter pw) {
            pw.append("TimeFrame:[")
                    .append(sDateFormat.format(new Date(mStart)))
                    .append(" - ")
                    .append(sDateFormat.format(new Date(mEnd)))
                    .println("]:");
            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
            if (mCounters.isEmpty()) {
                ipw.println("NO EVENTS");
            } else {
                for (Map.Entry<PluginType<?>, EventCounter> entry: mCounters.entrySet()) {
                    ipw.append(entry.getKey().mName).append(" -> {");
                    entry.getValue().dump(ipw);
                    ipw.println("}");
                }
            }
        }
    }

    private static class EventCounter {
        private int mEventCounter = 0;
        private int mFastEventCounter = 0;

        private void increase(long timestamp, long previousTimestamp) {
            mEventCounter++;
            if (timestamp - previousTimestamp < MIN_EVENT_DELAY) {
                mFastEventCounter++;
            }
        }

        private void dump(PrintWriter pw) {
            pw.append("Count:").append(String.valueOf(mEventCounter))
                    .append("; Fast:").append(String.valueOf(mFastEventCounter));
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.tools.r8.keepanno.annotations.KeepForApi;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@@ -39,6 +40,8 @@ public class PluginStorage {
    private final Map<PluginType<?>, Object> mValues = new HashMap<>();
    @GuardedBy("mLock")
    private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
    @GuardedBy("mLock")
    private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();

    /**
     * Updates value in storage and forwards it to corresponding listeners.
@@ -50,6 +53,7 @@ public class PluginStorage {
        Set<PluginManager.PluginChangeListener<T>> localListeners;
        synchronized (mLock) {
            mValues.put(type, value);
            mPluginEventStorage.onValueUpdated(type);
            ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
            localListeners = new LinkedHashSet<>(container.mListeners);
        }
@@ -91,13 +95,19 @@ public class PluginStorage {
        Map<PluginType<?>, Object> localValues;
        @SuppressWarnings("rawtypes")
        Map<PluginType, Set> localListeners = new HashMap<>();
        List<PluginEventStorage.TimeFrame> timeFrames;
        synchronized (mLock) {
            timeFrames = mPluginEventStorage.getTimeFrames();
            localValues = new HashMap<>(mValues);
            mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
        }
        pw.println("PluginStorage:");
        pw.println("values=" + localValues);
        pw.println("listeners=" + localListeners);
        pw.println("PluginEventStorage:");
        for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
            timeFrame.dump(pw);
        }
    }

    @GuardedBy("mLock")