diff options
| -rw-r--r-- | core/java/android/os/FileUtils.java | 45 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/FileUtilsTest.java | 116 | ||||
| -rw-r--r-- | packages/Shell/src/com/android/shell/BugreportReceiver.java | 42 |
3 files changed, 155 insertions, 48 deletions
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 2bec1c176905..9666d9af8cc4 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,6 +16,8 @@ package android.os; +import android.util.Log; + import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -25,6 +27,8 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; +import java.util.Comparator; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; @@ -34,6 +38,8 @@ import java.util.zip.CheckedInputStream; * @hide */ public class FileUtils { + private static final String TAG = "FileUtils"; + public static final int S_IRWXU = 00700; public static final int S_IRUSR = 00400; public static final int S_IWUSR = 00200; @@ -161,7 +167,8 @@ public class FileUtils { } else if (max < 0) { // "tail" mode: keep the last N int len; boolean rolled = false; - byte[] last = null, data = null; + byte[] last = null; + byte[] data = null; do { if (last != null) rolled = true; byte[] tmp = last; last = data; data = tmp; @@ -237,4 +244,40 @@ public class FileUtils { } } } + + /** + * Delete older files in a directory until only those matching the given + * constraints remain. + * + * @param minCount Always keep at least this many files. + * @param minAge Always keep files younger than this age. + */ + public static void deleteOlderFiles(File dir, int minCount, long minAge) { + if (minCount < 0 || minAge < 0) { + throw new IllegalArgumentException("Constraints must be positive or 0"); + } + + final File[] files = dir.listFiles(); + if (files == null) return; + + // Sort with newest files first + Arrays.sort(files, new Comparator<File>() { + @Override + public int compare(File lhs, File rhs) { + return (int) (rhs.lastModified() - lhs.lastModified()); + } + }); + + // Keep at least minCount files + for (int i = minCount; i < files.length; i++) { + final File file = files[i]; + + // Keep files newer than minAge + final long age = System.currentTimeMillis() - file.lastModified(); + if (age > minAge) { + Log.d(TAG, "Deleting old file " + file); + file.delete(); + } + } + } } diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 4d0b89258412..0f2b80371db9 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -16,61 +16,65 @@ package android.os; +import static android.text.format.DateUtils.DAY_IN_MILLIS; +import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.WEEK_IN_MILLIS; + import android.content.Context; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; +import com.google.android.collect.Sets; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; +import java.util.Arrays; +import java.util.HashSet; +import libcore.io.IoUtils; + +@MediumTest public class FileUtilsTest extends AndroidTestCase { private static final String TEST_DATA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private File mDir; private File mTestFile; private File mCopyFile; @Override protected void setUp() throws Exception { super.setUp(); - File testDir = getContext().getDir("testing", Context.MODE_PRIVATE); - mTestFile = new File(testDir, "test.file"); - mCopyFile = new File(testDir, "copy.file"); - FileWriter writer = new FileWriter(mTestFile); - try { - writer.write(TEST_DATA, 0, TEST_DATA.length()); - } finally { - writer.close(); - } + mDir = getContext().getDir("testing", Context.MODE_PRIVATE); + mTestFile = new File(mDir, "test.file"); + mCopyFile = new File(mDir, "copy.file"); } @Override protected void tearDown() throws Exception { - if (mTestFile.exists()) mTestFile.delete(); - if (mCopyFile.exists()) mCopyFile.delete(); + IoUtils.deleteContents(mDir); } // TODO: test setPermissions(), getPermissions() - @MediumTest public void testCopyFile() throws Exception { + stageFile(mTestFile, TEST_DATA); assertFalse(mCopyFile.exists()); FileUtils.copyFile(mTestFile, mCopyFile); assertTrue(mCopyFile.exists()); assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null)); } - @MediumTest public void testCopyToFile() throws Exception { final String s = "Foo Bar"; assertFalse(mCopyFile.exists()); - FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile); assertTrue(mCopyFile.exists()); + FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile); + assertTrue(mCopyFile.exists()); assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null)); } - @MediumTest public void testIsFilenameSafe() throws Exception { assertTrue(FileUtils.isFilenameSafe(new File("foobar"))); assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23"))); @@ -78,8 +82,9 @@ public class FileUtilsTest extends AndroidTestCase { assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar"))); } - @MediumTest public void testReadTextFile() throws Exception { + stageFile(mTestFile, TEST_DATA); + assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null)); assertEquals("ABCDE", FileUtils.readTextFile(mTestFile, 5, null)); @@ -97,8 +102,8 @@ public class FileUtilsTest extends AndroidTestCase { assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>")); } - @MediumTest public void testReadTextFileWithZeroLengthFile() throws Exception { + stageFile(mTestFile, TEST_DATA); new FileOutputStream(mTestFile).close(); // Zero out the file assertEquals("", FileUtils.readTextFile(mTestFile, 0, null)); assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>")); @@ -106,4 +111,81 @@ public class FileUtilsTest extends AndroidTestCase { assertEquals("", FileUtils.readTextFile(mTestFile, -1, "<>")); assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>")); } + + public void testDeleteOlderEmptyDir() throws Exception { + FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS); + assertDirContents(); + } + + public void testDeleteOlderTypical() throws Exception { + touch("file1", HOUR_IN_MILLIS); + touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + FileUtils.deleteOlderFiles(mDir, 3, DAY_IN_MILLIS); + assertDirContents("file1", "file2", "file3"); + } + + public void testDeleteOlderInFuture() throws Exception { + touch("file1", -HOUR_IN_MILLIS); + touch("file2", HOUR_IN_MILLIS); + touch("file3", WEEK_IN_MILLIS); + FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS); + assertDirContents("file1", "file2"); + + touch("file1", -HOUR_IN_MILLIS); + touch("file2", HOUR_IN_MILLIS); + touch("file3", WEEK_IN_MILLIS); + FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS); + assertDirContents("file1", "file2"); + } + + public void testDeleteOlderOnlyAge() throws Exception { + touch("file1", HOUR_IN_MILLIS); + touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS); + assertDirContents("file1"); + } + + public void testDeleteOlderOnlyCount() throws Exception { + touch("file1", HOUR_IN_MILLIS); + touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS); + FileUtils.deleteOlderFiles(mDir, 2, 0); + assertDirContents("file1", "file2"); + } + + private void touch(String name, long age) throws Exception { + final File file = new File(mDir, name); + file.createNewFile(); + file.setLastModified(System.currentTimeMillis() - age); + } + + private void stageFile(File file, String data) throws Exception { + FileWriter writer = new FileWriter(file); + try { + writer.write(data, 0, data.length()); + } finally { + writer.close(); + } + } + + private void assertDirContents(String... expected) { + final HashSet<String> expectedSet = Sets.newHashSet(expected); + String[] actual = mDir.list(); + if (actual == null) actual = new String[0]; + + assertEquals( + "Expected " + Arrays.toString(expected) + " but actual " + Arrays.toString(actual), + expected.length, actual.length); + for (String actualFile : actual) { + assertTrue("Unexpected actual file " + actualFile, expectedSet.contains(actualFile)); + } + } } diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java index 3b1ebf49490b..de04909e3e3f 100644 --- a/packages/Shell/src/com/android/shell/BugreportReceiver.java +++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java @@ -29,17 +29,16 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; +import android.os.FileUtils; import android.os.SystemProperties; import android.support.v4.content.FileProvider; -import android.util.Log; +import android.text.format.DateUtils; import android.util.Patterns; import com.google.android.collect.Lists; import java.io.File; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; /** * Receiver that handles finished bugreports, usually by attaching them to an @@ -54,10 +53,15 @@ public class BugreportReceiver extends BroadcastReceiver { private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT"; /** - * Number of bugreports to retain before deleting the oldest; 4 reports and - * 4 screenshots are roughly 17MB of disk space. + * Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are + * roughly 17MB of disk space. */ - private static final int NUM_OLD_FILES = 8; + private static final int MIN_KEEP_COUNT = 8; + + /** + * Always keep bugreports taken in the last week. + */ + private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS; @Override public void onReceive(Context context, Intent intent) { @@ -94,7 +98,8 @@ public class BugreportReceiver extends BroadcastReceiver { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { - deleteOlderFiles(bugreportFile.getParentFile(), NUM_OLD_FILES); + FileUtils.deleteOlderFiles( + bugreportFile.getParentFile(), MIN_KEEP_COUNT, MIN_KEEP_AGE); result.finish(); return null; } @@ -164,28 +169,6 @@ public class BugreportReceiver extends BroadcastReceiver { return foundAccount; } - /** - * Delete the oldest files in given directory until only the requested - * number remain. - */ - private static void deleteOlderFiles(File dir, int retainNum) { - final File[] files = dir.listFiles(); - if (files == null) return; - - Arrays.sort(files, new ModifiedComparator()); - for (int i = retainNum; i < files.length; i++) { - Log.d(TAG, "Deleting old file " + files[i]); - files[i].delete(); - } - } - - private static class ModifiedComparator implements Comparator<File> { - @Override - public int compare(File lhs, File rhs) { - return (int) (rhs.lastModified() - lhs.lastModified()); - } - } - private static File getFileExtra(Intent intent, String key) { final String path = intent.getStringExtra(key); if (path != null) { @@ -194,5 +177,4 @@ public class BugreportReceiver extends BroadcastReceiver { return null; } } - } |