Loading core/java/android/os/FileUtils.java +44 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os; import android.util.Log; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); } } } } core/tests/coretests/src/android/os/FileUtilsTest.java +99 −17 Original line number Diff line number Diff line Loading @@ -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"))); Loading @@ -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)); Loading @@ -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, "<>")); Loading @@ -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)); } } } packages/Shell/src/com/android/shell/BugreportReceiver.java +12 −30 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -194,5 +177,4 @@ public class BugreportReceiver extends BroadcastReceiver { return null; } } } Loading
core/java/android/os/FileUtils.java +44 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os; import android.util.Log; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); } } } }
core/tests/coretests/src/android/os/FileUtilsTest.java +99 −17 Original line number Diff line number Diff line Loading @@ -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"))); Loading @@ -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)); Loading @@ -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, "<>")); Loading @@ -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)); } } }
packages/Shell/src/com/android/shell/BugreportReceiver.java +12 −30 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -194,5 +177,4 @@ public class BugreportReceiver extends BroadcastReceiver { return null; } } }