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

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

Merge "UsageStats creates too many daily/weekly/monthly/yearly files."

parents 1ee95abf b21d59f2
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -23,12 +23,14 @@ import static junit.framework.TestCase.fail;
import static org.testng.Assert.assertEquals;

import android.app.usage.EventList;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.res.Configuration;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;

import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -38,6 +40,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
@@ -448,4 +451,45 @@ public class UsageStatsDatabaseTest {
        runBackupRestoreTest(0);
        runBackupRestoreTest(99999);
    }

    /**
     * Test the pruning in indexFilesLocked() that only allow up to 100 daily files, 50 weekly files
     * , 12 monthly files, 10 yearly files.
     */
    @Test
    public void testMaxFiles() throws IOException {
        final File[] intervalDirs = new File[]{
            new File(mTestDir, "daily"),
            new File(mTestDir, "weekly"),
            new File(mTestDir, "monthly"),
            new File(mTestDir, "yearly"),
        };
        // Create 10 extra files under each interval dir.
        final int extra = 10;
        final int length = intervalDirs.length;
        for (int i = 0; i < length; i++) {
            final int numFiles = UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i] + extra;
            for (int f = 0; f < numFiles; f++) {
                final AtomicFile file = new AtomicFile(new File(intervalDirs[i], Long.toString(f)));
                FileOutputStream fos = file.startWrite();
                fos.write(1);
                file.finishWrite(fos);
            }
        }
        // indexFilesLocked() list files under each interval dir, if number of files are more than
        // the max allowed files for each interval type, it deletes the lowest numbered files.
        mUsageStatsDatabase.forceIndexFiles();
        final int len = mUsageStatsDatabase.mSortedStatFiles.length;
        for (int i = 0; i < len; i++) {
            final TimeSparseArray<AtomicFile> files =  mUsageStatsDatabase.mSortedStatFiles[i];
            // The stats file for each interval type equals to max allowed.
            assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i],
                    files.size());
            // The highest numbered file,
            assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i] + extra - 1,
                    files.keyAt(files.size() - 1));
            // The lowest numbered file:
            assertEquals(extra, files.keyAt(0));
        }
    }
}
+31 −9
Original line number Diff line number Diff line
@@ -43,8 +43,8 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
@@ -84,6 +84,9 @@ public class UsageStatsDatabase {
    @VisibleForTesting
    public static final int BACKUP_VERSION = 4;

    @VisibleForTesting
    static final int[] MAX_FILES_PER_INTERVAL_TYPE = new int[]{100, 50, 12, 10};

    // Key under which the payload blob is stored
    // same as UsageStatsBackupHelper.KEY_USAGE_STATS
    static final String KEY_USAGE_STATS = "usage_stats";
@@ -104,7 +107,8 @@ public class UsageStatsDatabase {

    private final Object mLock = new Object();
    private final File[] mIntervalDirs;
    private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
    @VisibleForTesting
    final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
    private final UnixCalendar mCal;
    private final File mVersionFile;
    private final File mBackupsDir;
@@ -248,6 +252,14 @@ public class UsageStatsDatabase {
        return true;
    }

    /** @hide */
    @VisibleForTesting
    void forceIndexFiles() {
        synchronized (mLock) {
            indexFilesLocked();
        }
    }

    private void indexFilesLocked() {
        final FilenameFilter backupFileFilter = new FilenameFilter() {
            @Override
@@ -255,7 +267,6 @@ public class UsageStatsDatabase {
                return !name.endsWith(BAK_SUFFIX);
            }
        };

        // Index the available usage stat files on disk.
        for (int i = 0; i < mSortedStatFiles.length; i++) {
            if (mSortedStatFiles[i] == null) {
@@ -268,8 +279,9 @@ public class UsageStatsDatabase {
                if (DEBUG) {
                    Slog.d(TAG, "Found " + files.length + " stat files for interval " + i);
                }

                for (File f : files) {
                final int len = files.length;
                for (int j = 0; j < len; j++) {
                    final File f = files[j];
                    final AtomicFile af = new AtomicFile(f);
                    try {
                        mSortedStatFiles[i].put(parseBeginTime(af), af);
@@ -277,6 +289,16 @@ public class UsageStatsDatabase {
                        Slog.e(TAG, "failed to index file: " + f, e);
                    }
                }

                // only keep the max allowed number of files for each interval type.
                final int toDelete = mSortedStatFiles[i].size() - MAX_FILES_PER_INTERVAL_TYPE[i];
                if (toDelete > 0) {
                    for (int j = 0; j < toDelete; j++) {
                        mSortedStatFiles[i].valueAt(0).delete();
                        mSortedStatFiles[i].removeAt(0);
                    }
                    Slog.d(TAG, "Deleted " + toDelete + " stat files for interval " + i);
                }
            }
        }
    }
+2 −2
Original line number Diff line number Diff line
@@ -595,8 +595,8 @@ class UserUsageStatsService {
    private void loadActiveStats(final long currentTimeMillis) {
        for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
            final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
            if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
                    currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
            if (stats != null
                    && currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
                if (DEBUG) {
                    Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
                            sDateFormat.format(stats.beginTime) + "(" + stats.beginTime +