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

Commit 2811d924 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Guarding collisions in TimeSparseArray

TimeSparseArray - used to store UsageEvents - can keep at most one event
per millisecond, which can result in an event being replaced by another
event that occurred close enough that the system records it at the same
millisecond.

Test: atest android.app.usage.TimeSparseArrayTest

Fixes: 73832306
Change-Id: I860a101ab098f65d5c5832758832f43572865690
parent 5d9617c4
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.app.usage;

import android.util.LongSparseArray;
import android.util.Slog;

/**
 * An array that indexes by a long timestamp, representing milliseconds since the epoch.
@@ -24,6 +25,8 @@ import android.util.LongSparseArray;
 * {@hide}
 */
public class TimeSparseArray<E> extends LongSparseArray<E> {
    private static final String TAG = TimeSparseArray.class.getSimpleName();

    public TimeSparseArray() {
        super();
    }
@@ -69,6 +72,25 @@ public class TimeSparseArray<E> extends LongSparseArray<E> {
        }
    }

    /**
     * {@inheritDoc}
     *
     * Overridden to ensure no collisions. The key (time in milliseconds) is incremented till an
     * empty place is found.
     */
    @Override
    public void put(long key, E value) {
        final long origKey = key;
        while (indexOfKey(key) >= 0) {
            key++;
        }
        if (origKey != key) {
            Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
                    + " displaced to " + key);
        }
        super.put(key, value);
    }

    /**
     * Finds the index of the first element whose timestamp is less than or equal to
     * the given time.
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.app.usage;

import static org.junit.Assert.assertTrue;

import android.os.SystemClock;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

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

@RunWith(AndroidJUnit4.class)
@SmallTest
public class TimeSparseArrayTest {
    @Test
    public void testDuplicateKeysNotDropped() {
        final TimeSparseArray<Integer> testTimeSparseArray = new TimeSparseArray<>();
        final long key = SystemClock.elapsedRealtime();
        for (int i = 0; i < 5; i++) {
            testTimeSparseArray.put(key, i);
        }
        for (int i = 0; i < 5; i++) {
            final int valueIndex = testTimeSparseArray.indexOfValue(i);
            assertTrue("Value " + i + " not found; intended key: " + key , valueIndex >= 0);
            final long keyForValue = testTimeSparseArray.keyAt(valueIndex);
            assertTrue("Value " + i + " stored too far (at " + keyForValue + ") from intended key "
                    + key, Math.abs(keyForValue - key) < 100);
        }
    }
}