Merge "Move deleteOlderFiles() to framework, tests." into jb-mr2-dev
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user