Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit debbeec5 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Move deleteOlderFiles() to framework, tests." into jb-mr2-dev

parents b1b58023 d9526907
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -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();
            }
        }
    }
}
+99 −17
Original line number Diff line number Diff line
@@ -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));
        }
    }
}
+12 −30
Original line number Diff line number Diff line
@@ -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;
        }
    }

}