All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.am.BaseAppStateTimeSlotEvents Maven / Gradle / Ivy

/*
 * Copyright (C) 2022 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.am;

import android.annotation.NonNull;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * Base class to track certain individual event of app states, it groups the events into time-based
 * slots, thus we could only track the total number of events in a slot, eliminating
 * the needs to track the timestamps for each individual event. This will be much more memory
 * efficient for the case of massive amount of events.
 */
class BaseAppStateTimeSlotEvents extends BaseAppStateEvents {

    static final boolean DEBUG_BASE_APP_TIME_SLOT_EVENTS = false;

    /**
     * The size (in ms) of the timeslot, should be greater than 0 always.
     */
    final long mTimeSlotSize;

    /**
     * The start timestamp of current timeslot.
     */
    long[] mCurSlotStartTime;

    BaseAppStateTimeSlotEvents(int uid, @NonNull String packageName, int numOfEventTypes,
            long timeslotSize, @NonNull String tag,
            @NonNull MaxTrackingDurationConfig maxTrackingDurationConfig) {
        super(uid, packageName, numOfEventTypes, tag, maxTrackingDurationConfig);
        mTimeSlotSize = timeslotSize;
        mCurSlotStartTime = new long[numOfEventTypes];
    }

    BaseAppStateTimeSlotEvents(@NonNull BaseAppStateTimeSlotEvents other) {
        super(other);
        mTimeSlotSize = other.mTimeSlotSize;
        mCurSlotStartTime = new long[other.mCurSlotStartTime.length];
        for (int i = 0; i < mCurSlotStartTime.length; i++) {
            mCurSlotStartTime[i] = other.mCurSlotStartTime[i];
        }
    }

    @Override
    LinkedList add(LinkedList events, LinkedList otherEvents) {
        if (DEBUG_BASE_APP_TIME_SLOT_EVENTS) {
            Slog.wtf(mTag, "Called into BaseAppStateTimeSlotEvents#add unexpected.");
        }
        // This function is invalid semantically here without the information of time-bases.
        return null;
    }

    @Override
    void add(BaseAppStateEvents otherObj) {
        if (otherObj == null || !(otherObj instanceof BaseAppStateTimeSlotEvents)) {
            return;
        }
        final BaseAppStateTimeSlotEvents other = (BaseAppStateTimeSlotEvents) otherObj;
        if (mEvents.length != other.mEvents.length) {
            if (DEBUG_BASE_APP_TIME_SLOT_EVENTS) {
                Slog.wtf(mTag, "Incompatible event table this=" + this + ", other=" + other);
            }
            return;
        }
        for (int i = 0; i < mEvents.length; i++) {
            final LinkedList otherEvents = other.mEvents[i];
            if (otherEvents == null || otherEvents.size() == 0) {
                continue;
            }
            LinkedList events = mEvents[i];
            if (events == null || events.size() == 0) {
                mEvents[i] = new LinkedList(otherEvents);
                mCurSlotStartTime[i] = other.mCurSlotStartTime[i];
                continue;
            }

            final LinkedList dest = new LinkedList<>();
            final Iterator itl = events.iterator();
            final Iterator itr = otherEvents.iterator();
            final long maxl = mCurSlotStartTime[i];
            final long maxr = other.mCurSlotStartTime[i];
            final long minl = maxl - mTimeSlotSize * (events.size() - 1);
            final long minr = maxr - mTimeSlotSize * (otherEvents.size() - 1);
            final long latest = Math.max(maxl, maxr);
            final long earliest = Math.min(minl, minr);
            for (long start = earliest; start <= latest; start += mTimeSlotSize) {
                dest.add((start >= minl && start <= maxl ? itl.next() : 0)
                        + (start >= minr && start <= maxr ? itr.next() : 0));
            }
            mEvents[i] = dest;
            if (maxl < maxr) {
                mCurSlotStartTime[i] = other.mCurSlotStartTime[i];
            }
            trimEvents(getEarliest(mCurSlotStartTime[i]), i);
        }
    }

    @Override
    int getTotalEventsSince(long since, long now, int index) {
        final LinkedList events = mEvents[index];
        if (events == null || events.size() == 0) {
            return 0;
        }
        final long start = getSlotStartTime(since);
        if (start > mCurSlotStartTime[index]) {
            return 0;
        }
        final long end = Math.min(getSlotStartTime(now), mCurSlotStartTime[index]);
        final Iterator it = events.descendingIterator();
        int count = 0;
        for (long time = mCurSlotStartTime[index]; time >= start && it.hasNext();
                time -= mTimeSlotSize) {
            final int val = it.next();
            if (time <= end) {
                count += val;
            }
        }
        return count;
    }

    void addEvent(long now, int index) {
        final long slot = getSlotStartTime(now);
        if (DEBUG_BASE_APP_TIME_SLOT_EVENTS) {
            Slog.i(mTag, "Adding event to slot " + slot);
        }
        LinkedList events = mEvents[index];
        if (events == null) {
            events = new LinkedList();
            mEvents[index] = events;
        }
        if (events.size() == 0) {
            events.add(1);
        } else {
            for (long start = mCurSlotStartTime[index]; start < slot; start += mTimeSlotSize) {
                events.add(0);
            }
            events.offerLast(events.pollLast() + 1);
        }
        mCurSlotStartTime[index] = slot;
        trimEvents(getEarliest(now), index);
    }

    @Override
    void trimEvents(long earliest, int index) {
        final LinkedList events = mEvents[index];
        if (events == null || events.size() == 0) {
            return;
        }
        final long slot = getSlotStartTime(earliest);
        for (long time = mCurSlotStartTime[index] - mTimeSlotSize * (events.size() - 1);
                time < slot && events.size() > 0; time += mTimeSlotSize) {
            events.pop();
        }
    }

    long getSlotStartTime(long timestamp) {
        return timestamp - timestamp % mTimeSlotSize;
    }

    @VisibleForTesting
    long getCurrentSlotStartTime(int index) {
        return mCurSlotStartTime[index];
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy