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

Commit c0bcdfdb authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Removing TimeSparseArray"

parents 462c09e3 2c3dc9b1
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -208,6 +208,39 @@ public class LongSparseArray<E> implements Cloneable {
        // Log.e("SparseArray", "gc end with " + mSize);
    }

    /**
     * Returns the index of the first element whose key is greater than or equal to the given key.
     *
     * @param key The key for which to search the array.
     * @return The smallest {@code index} for which {@code (keyAt(index) >= key)} is
     * {@code true}, or {@link #size() size} if no such {@code index} exists.
     * @hide
     */
    public int firstIndexOnOrAfter(long key) {
        if (mGarbage) {
            gc();
        }
        final int index = Arrays.binarySearch(mKeys, 0, size(), key);
        return (index >= 0) ? index : -index - 1;
    }

    /**
     * Returns the index of the last element whose key is less than or equal to the given key.
     *
     * @param key The key for which to search the array.
     * @return The largest {@code index} for which {@code (keyAt(index) <= key)} is
     * {@code true}, or {@code -1} if no such {@code index} exists.
     * @hide
     */
    public int lastIndexOnOrBefore(long key) {
        final int index = firstIndexOnOrAfter(key);

        if (index < size() && keyAt(index) == key) {
            return index;
        }
        return index - 1;
    }

    /**
     * Adds a mapping from the specified key to the specified value,
     * replacing the previous mapping from the specified key if there
+0 −99
Original line number Diff line number Diff line
/**
 * 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 android.util;

import java.util.Objects;

/**
 * An array that indexes by a long timestamp, representing milliseconds since the epoch.
 * @param <E> The type of values this container maps to a timestamp.
 *
 * {@hide}
 */
public class TimeSparseArray<E> extends LongSparseArray<E> {
    private static final String TAG = TimeSparseArray.class.getSimpleName();

    private boolean mWtfReported;

    /**
     * Finds the index of the first element whose timestamp is greater or equal to
     * the given time.
     *
     * @param time The timestamp for which to search the array.
     * @return The smallest {@code index} for which {@code (keyAt(index) >= timeStamp)} is
     * {@code true}, or {@link #size() size} if no such {@code index} exists.
     */
    public int closestIndexOnOrAfter(long time) {
        final int size = size();
        int result = size;
        int lo = 0;
        int hi = size - 1;
        while (lo <= hi) {
            final int mid = lo + ((hi - lo) / 2);
            final long key = keyAt(mid);

            if (time > key) {
                lo = mid + 1;
            } else if (time < key) {
                hi = mid - 1;
                result = mid;
            } else {
                return mid;
            }
        }
        return result;
    }

    /**
     * {@inheritDoc}
     *
     * <p> This container can store only one value for each timestamp. And so ideally, the caller
     * should ensure that there are no collisions. Reporting a {@link Slog#wtf(String, String)}
     * if that happens, as that will lead to the previous value being overwritten.
     */
    @Override
    public void put(long key, E value) {
        final int index = indexOfKey(key);
        if (index >= 0) {
            final E curValue = valueAt(index);
            if (Objects.equals(curValue, value)) {
                Log.w(TAG, "Overwriting value at " + key + " by equal value " + value);
            } else if (!mWtfReported) {
                Slog.wtf(TAG, "Overwriting value " + curValue + " by " + value + " at " + key);
                mWtfReported = true;
            }
        }
        super.put(key, value);
    }

    /**
     * Finds the index of the first element whose timestamp is less than or equal to
     * the given time.
     *
     * @param time The timestamp for which to search the array.
     * @return The largest {@code index} for which {@code (keyAt(index) <= timeStamp)} is
     * {@code true}, or -1 if no such {@code index} exists.
     */
    public int closestIndexOnOrBefore(long time) {
        final int index = closestIndexOnOrAfter(time);

        if (index < size() && keyAt(index) == time) {
            return index;
        }
        return index - 1;
    }
}
+69 −0
Original line number Diff line number Diff line
@@ -51,4 +51,73 @@ public class LongSparseArrayTest {
                    .isFalse();
        }
    }

    @Test
    public void firstIndexOnOrAfter() {
        final LongSparseArray<Object> longSparseArray = new LongSparseArray<>();

        // Values don't matter for this test.
        longSparseArray.put(51, new Object());
        longSparseArray.put(10, new Object());
        longSparseArray.put(59, new Object());

        assertThat(longSparseArray.size()).isEqualTo(3);

        // Testing any number arbitrarily smaller than 10.
        assertThat(longSparseArray.firstIndexOnOrAfter(-141213)).isEqualTo(0);
        for (long time = -43; time <= 10; time++) {
            assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(0);
        }

        for (long time = 11; time <= 51; time++) {
            assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(1);
        }

        for (long time = 52; time <= 59; time++) {
            assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(2);
        }

        for (long time = 60; time <= 102; time++) {
            assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(3);
        }
        // Testing any number arbitrarily larger than 59.
        assertThat(longSparseArray.firstIndexOnOrAfter(15332)).isEqualTo(3);
    }

    @Test
    public void lastIndexOnOrBefore() {
        final LongSparseArray<Object> longSparseArray = new LongSparseArray<>();

        // Values don't matter for this test.
        longSparseArray.put(21, new Object());
        longSparseArray.put(4, new Object());
        longSparseArray.put(91, new Object());
        longSparseArray.put(39, new Object());

        assertThat(longSparseArray.size()).isEqualTo(4);

        // Testing any number arbitrarily smaller than 4.
        assertThat(longSparseArray.lastIndexOnOrBefore(-1478133)).isEqualTo(-1);
        for (long time = -42; time < 4; time++) {
            assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(-1);
        }

        for (long time = 4; time < 21; time++) {
            assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(0);
        }

        for (long time = 21; time < 39; time++) {
            assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(1);
        }

        for (long time = 39; time < 91; time++) {
            assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(2);
        }

        for (long time = 91; time < 109; time++) {
            assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(3);
        }
        // Testing any number arbitrarily larger than 91.
        assertThat(longSparseArray.lastIndexOnOrBefore(1980732)).isEqualTo(3);
    }
}
+0 −103
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.util;

import static com.google.common.truth.Truth.assertThat;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Tests for {@link TimeSparseArray}.
 * This class only tests subclass specific functionality. Tests for the super class
 * {@link LongSparseArray} should be covered under {@link LongSparseArrayTest}.
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TimeSparseArrayTest {

    @Test
    public void closestIndexOnOrAfter() {
        final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();

        // Values don't matter for this test.
        timeSparseArray.put(51, new Object());
        timeSparseArray.put(10, new Object());
        timeSparseArray.put(59, new Object());

        assertThat(timeSparseArray.size()).isEqualTo(3);

        // Testing any number arbitrarily smaller than 10.
        assertThat(timeSparseArray.closestIndexOnOrAfter(-141213)).isEqualTo(0);
        for (long time = -43; time <= 10; time++) {
            assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(0);
        }

        for (long time = 11; time <= 51; time++) {
            assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(1);
        }

        for (long time = 52; time <= 59; time++) {
            assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(2);
        }

        for (long time = 60; time <= 102; time++) {
            assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(3);
        }
        // Testing any number arbitrarily larger than 59.
        assertThat(timeSparseArray.closestIndexOnOrAfter(15332)).isEqualTo(3);
    }

    @Test
    public void closestIndexOnOrBefore() {
        final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();

        // Values don't matter for this test.
        timeSparseArray.put(21, new Object());
        timeSparseArray.put(4, new Object());
        timeSparseArray.put(91, new Object());
        timeSparseArray.put(39, new Object());

        assertThat(timeSparseArray.size()).isEqualTo(4);

        // Testing any number arbitrarily smaller than 4.
        assertThat(timeSparseArray.closestIndexOnOrBefore(-1478133)).isEqualTo(-1);
        for (long time = -42; time < 4; time++) {
            assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(-1);
        }

        for (long time = 4; time < 21; time++) {
            assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(0);
        }

        for (long time = 21; time < 39; time++) {
            assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(1);
        }

        for (long time = 39; time < 91; time++) {
            assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(2);
        }

        for (long time = 91; time < 109; time++) {
            assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(3);
        }
        // Testing any number arbitrarily larger than 91.
        assertThat(timeSparseArray.closestIndexOnOrBefore(1980732)).isEqualTo(3);
    }
}
+18 −18
Original line number Diff line number Diff line
@@ -32,12 +32,12 @@ import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeSparseArray;
import android.util.TimeUtils;

import com.android.internal.annotations.VisibleForTesting;
@@ -74,12 +74,12 @@ public class CpuWakeupStats {
    private final WakingActivityHistory mRecentWakingActivity;

    @VisibleForTesting
    final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
    final LongSparseArray<Wakeup> mWakeupEvents = new LongSparseArray<>();

    /* Maps timestamp -> {subsystem  -> {uid -> procState}} */
    @VisibleForTesting
    final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
            new TimeSparseArray<>();
    final LongSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
            new LongSparseArray<>();

    final SparseIntArray mUidProcStates = new SparseIntArray();
    private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4);
@@ -218,11 +218,11 @@ public class CpuWakeupStats {
        // the last wakeup and its attribution (if computed) is always stored, even if that wakeup
        // had occurred before retentionDuration.
        final long retentionDuration = mConfig.WAKEUP_STATS_RETENTION_MS;
        int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
        int lastIdx = mWakeupEvents.lastIndexOnOrBefore(elapsedRealtime - retentionDuration);
        for (int i = lastIdx; i >= 0; i--) {
            mWakeupEvents.removeAt(i);
        }
        lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
        lastIdx = mWakeupAttribution.lastIndexOnOrBefore(elapsedRealtime - retentionDuration);
        for (int i = lastIdx; i >= 0; i--) {
            mWakeupAttribution.removeAt(i);
        }
@@ -273,9 +273,9 @@ public class CpuWakeupStats {
            SparseIntArray uidProcStates) {
        final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS;

        final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
        final int startIdx = mWakeupEvents.firstIndexOnOrAfter(
                activityElapsed - matchingWindowMillis);
        final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
        final int endIdx = mWakeupEvents.lastIndexOnOrBefore(
                activityElapsed + matchingWindowMillis);

        for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) {
@@ -404,7 +404,7 @@ public class CpuWakeupStats {
    static final class WakingActivityHistory {
        private LongSupplier mRetentionSupplier;
        @VisibleForTesting
        final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>();
        final SparseArray<LongSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>();

        WakingActivityHistory(LongSupplier retentionSupplier) {
            mRetentionSupplier = retentionSupplier;
@@ -414,9 +414,9 @@ public class CpuWakeupStats {
            if (uidProcStates == null) {
                return;
            }
            TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
            LongSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
            if (wakingActivity == null) {
                wakingActivity = new TimeSparseArray<>();
                wakingActivity = new LongSparseArray<>();
                mWakingActivity.put(subsystem, wakingActivity);
            }
            final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime);
@@ -435,7 +435,7 @@ public class CpuWakeupStats {
            // Limit activity history per subsystem to the last retention period as supplied by
            // mRetentionSupplier. Note that the last activity is always present, even if it
            // occurred before the retention period.
            final int endIdx = wakingActivity.closestIndexOnOrBefore(
            final int endIdx = wakingActivity.lastIndexOnOrBefore(
                    elapsedRealtime - mRetentionSupplier.getAsLong());
            for (int i = endIdx; i >= 0; i--) {
                wakingActivity.removeAt(i);
@@ -445,11 +445,11 @@ public class CpuWakeupStats {
        SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
            final SparseIntArray uidsToReturn = new SparseIntArray();

            final TimeSparseArray<SparseIntArray> activityForSubsystem =
            final LongSparseArray<SparseIntArray> activityForSubsystem =
                    mWakingActivity.get(subsystem);
            if (activityForSubsystem != null) {
                final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
                final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
                final int startIdx = activityForSubsystem.firstIndexOnOrAfter(startElapsed);
                final int endIdx = activityForSubsystem.lastIndexOnOrBefore(endElapsed);
                for (int i = endIdx; i >= startIdx; i--) {
                    final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i);
                    for (int j = 0; j < uidsForTime.size(); j++) {
@@ -463,8 +463,8 @@ public class CpuWakeupStats {
                    activityForSubsystem.removeAt(i);
                }
                // Generally waking activity is a high frequency occurrence for any subsystem, so we
                // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
                // This will leave one TimeSparseArray per subsystem, which are few right now.
                // don't delete the LongSparseArray even if it is now empty, to avoid object churn.
                // This will leave one LongSparseArray per subsystem, which are few right now.
            }
            return uidsToReturn.size() > 0 ? uidsToReturn : null;
        }
@@ -474,7 +474,7 @@ public class CpuWakeupStats {
            pw.increaseIndent();
            for (int i = 0; i < mWakingActivity.size(); i++) {
                pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
                final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
                final LongSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
                if (wakingActivity == null) {
                    continue;
                }
Loading