Loading services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +44 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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)); } } } services/usage/java/com/android/server/usage/UsageStatsDatabase.java +31 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading @@ -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; Loading Loading @@ -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 Loading @@ -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) { Loading @@ -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); Loading @@ -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); } } } } Loading services/usage/java/com/android/server/usage/UserUsageStatsService.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 + Loading Loading
services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +44 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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)); } } }
services/usage/java/com/android/server/usage/UsageStatsDatabase.java +31 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading @@ -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; Loading Loading @@ -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 Loading @@ -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) { Loading @@ -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); Loading @@ -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); } } } } Loading
services/usage/java/com/android/server/usage/UserUsageStatsService.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 + Loading