summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/FileUtils.java45
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java116
-rw-r--r--packages/Shell/src/com/android/shell/BugreportReceiver.java42
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;
}
}
-
}