diff options
| -rw-r--r-- | services/core/java/com/android/server/storage/FileCollector.java | 218 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/storage/FileCollectorTest.java | 141 |
2 files changed, 359 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/storage/FileCollector.java b/services/core/java/com/android/server/storage/FileCollector.java new file mode 100644 index 000000000000..b96eb694897b --- /dev/null +++ b/services/core/java/com/android/server/storage/FileCollector.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.server.storage; + +import android.annotation.IntDef; +import android.os.storage.StorageManager; +import android.util.ArrayMap; + +import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Map; + +/** + * FileCollector walks over a directory and categorizes storage usage by their type. + */ +public class FileCollector { + private static final int UNRECOGNIZED = -1; + private static final int IMAGES = 0; + private static final int VIDEO = 1; + private static final int AUDIO = 2; + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + UNRECOGNIZED, + IMAGES, + VIDEO, + AUDIO }) + private @interface FileTypes {} + + + private static final Map<String, Integer> EXTENSION_MAP = new ArrayMap<String, Integer>(); + static { + // Audio + EXTENSION_MAP.put("aac", AUDIO); + EXTENSION_MAP.put("amr", AUDIO); + EXTENSION_MAP.put("awb", AUDIO); + EXTENSION_MAP.put("snd", AUDIO); + EXTENSION_MAP.put("flac", AUDIO); + EXTENSION_MAP.put("mp3", AUDIO); + EXTENSION_MAP.put("mpga", AUDIO); + EXTENSION_MAP.put("mpega", AUDIO); + EXTENSION_MAP.put("mp2", AUDIO); + EXTENSION_MAP.put("m4a", AUDIO); + EXTENSION_MAP.put("aif", AUDIO); + EXTENSION_MAP.put("aiff", AUDIO); + EXTENSION_MAP.put("aifc", AUDIO); + EXTENSION_MAP.put("gsm", AUDIO); + EXTENSION_MAP.put("mka", AUDIO); + EXTENSION_MAP.put("m3u", AUDIO); + EXTENSION_MAP.put("wma", AUDIO); + EXTENSION_MAP.put("wax", AUDIO); + EXTENSION_MAP.put("ra", AUDIO); + EXTENSION_MAP.put("rm", AUDIO); + EXTENSION_MAP.put("ram", AUDIO); + EXTENSION_MAP.put("pls", AUDIO); + EXTENSION_MAP.put("sd2", AUDIO); + EXTENSION_MAP.put("wav", AUDIO); + EXTENSION_MAP.put("ogg", AUDIO); + EXTENSION_MAP.put("oga", AUDIO); + // Video + EXTENSION_MAP.put("3gpp", VIDEO); + EXTENSION_MAP.put("3gp", VIDEO); + EXTENSION_MAP.put("3gpp2", VIDEO); + EXTENSION_MAP.put("3g2", VIDEO); + EXTENSION_MAP.put("avi", VIDEO); + EXTENSION_MAP.put("dl", VIDEO); + EXTENSION_MAP.put("dif", VIDEO); + EXTENSION_MAP.put("dv", VIDEO); + EXTENSION_MAP.put("fli", VIDEO); + EXTENSION_MAP.put("m4v", VIDEO); + EXTENSION_MAP.put("ts", VIDEO); + EXTENSION_MAP.put("mpeg", VIDEO); + EXTENSION_MAP.put("mpg", VIDEO); + EXTENSION_MAP.put("mpe", VIDEO); + EXTENSION_MAP.put("mp4", VIDEO); + EXTENSION_MAP.put("vob", VIDEO); + EXTENSION_MAP.put("qt", VIDEO); + EXTENSION_MAP.put("mov", VIDEO); + EXTENSION_MAP.put("mxu", VIDEO); + EXTENSION_MAP.put("webm", VIDEO); + EXTENSION_MAP.put("lsf", VIDEO); + EXTENSION_MAP.put("lsx", VIDEO); + EXTENSION_MAP.put("mkv", VIDEO); + EXTENSION_MAP.put("mng", VIDEO); + EXTENSION_MAP.put("asf", VIDEO); + EXTENSION_MAP.put("asx", VIDEO); + EXTENSION_MAP.put("wm", VIDEO); + EXTENSION_MAP.put("wmv", VIDEO); + EXTENSION_MAP.put("wmx", VIDEO); + EXTENSION_MAP.put("wvx", VIDEO); + EXTENSION_MAP.put("movie", VIDEO); + EXTENSION_MAP.put("wrf", VIDEO); + // Images + EXTENSION_MAP.put("bmp", IMAGES); + EXTENSION_MAP.put("gif", IMAGES); + EXTENSION_MAP.put("jpg", IMAGES); + EXTENSION_MAP.put("jpeg", IMAGES); + EXTENSION_MAP.put("jpe", IMAGES); + EXTENSION_MAP.put("pcx", IMAGES); + EXTENSION_MAP.put("png", IMAGES); + EXTENSION_MAP.put("svg", IMAGES); + EXTENSION_MAP.put("svgz", IMAGES); + EXTENSION_MAP.put("tiff", IMAGES); + EXTENSION_MAP.put("tif", IMAGES); + EXTENSION_MAP.put("wbmp", IMAGES); + EXTENSION_MAP.put("webp", IMAGES); + EXTENSION_MAP.put("dng", IMAGES); + EXTENSION_MAP.put("cr2", IMAGES); + EXTENSION_MAP.put("ras", IMAGES); + EXTENSION_MAP.put("art", IMAGES); + EXTENSION_MAP.put("jng", IMAGES); + EXTENSION_MAP.put("nef", IMAGES); + EXTENSION_MAP.put("nrw", IMAGES); + EXTENSION_MAP.put("orf", IMAGES); + EXTENSION_MAP.put("rw2", IMAGES); + EXTENSION_MAP.put("pef", IMAGES); + EXTENSION_MAP.put("psd", IMAGES); + EXTENSION_MAP.put("pnm", IMAGES); + EXTENSION_MAP.put("pbm", IMAGES); + EXTENSION_MAP.put("pgm", IMAGES); + EXTENSION_MAP.put("ppm", IMAGES); + EXTENSION_MAP.put("srw", IMAGES); + EXTENSION_MAP.put("arw", IMAGES); + EXTENSION_MAP.put("rgb", IMAGES); + EXTENSION_MAP.put("xbm", IMAGES); + EXTENSION_MAP.put("xpm", IMAGES); + EXTENSION_MAP.put("xwd", IMAGES); + } + + /** + * Returns the file categorization measurement result. + * @param path Directory to collect and categorize storage in. + */ + public static MeasurementResult getMeasurementResult(File path) { + return collectFiles(StorageManager.maybeTranslateEmulatedPathToInternal(path), + new MeasurementResult()); + } + + private static MeasurementResult collectFiles(File file, MeasurementResult result) { + File[] files = file.listFiles(); + + if (files == null) { + return result; + } + + for (File f : files) { + if (f.isDirectory()) { + try { + collectFiles(f, result); + } catch (StackOverflowError e) { + return result; + } + } else { + handleFile(result, f); + } + } + + return result; + } + + private static void handleFile(MeasurementResult result, File f) { + long fileSize = f.length(); + int fileType = EXTENSION_MAP.getOrDefault(getExtensionForFile(f), UNRECOGNIZED); + switch (fileType) { + case AUDIO: + result.audioSize += fileSize; + break; + case VIDEO: + result.videosSize += fileSize; + break; + case IMAGES: + result.imagesSize += fileSize; + break; + default: + result.miscSize += fileSize; + } + } + + private static String getExtensionForFile(File file) { + String fileName = file.getName(); + int index = fileName.lastIndexOf('.'); + if (index == -1) { + return ""; + } + return fileName.substring(index + 1).toLowerCase(); + } + + /** + * MeasurementResult contains a storage categorization result. + */ + public static class MeasurementResult { + public long imagesSize; + public long videosSize; + public long miscSize; + public long audioSize; + + /** + * Sums up the storage taken by all of the categorizable sizes in the measurement. + */ + public long totalAccountedSize() { + return imagesSize + videosSize + miscSize + audioSize; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/storage/FileCollectorTest.java b/services/tests/servicestests/src/com/android/server/storage/FileCollectorTest.java new file mode 100644 index 000000000000..f1b3442f1cf7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/storage/FileCollectorTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.storage; + +import android.test.AndroidTestCase; +import com.android.server.storage.FileCollector.MeasurementResult; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.PrintStream; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(JUnit4.class) +public class FileCollectorTest extends AndroidTestCase { + @Rule + public TemporaryFolder temporaryFolder; + + @Before + public void setUp() throws Exception { + temporaryFolder = new TemporaryFolder(); + temporaryFolder.create(); + } + + @Test + public void testEmpty() throws Exception { + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + assertThat(result.totalAccountedSize()).isEqualTo(0L); + } + + @Test + public void testImageFile() throws Exception { + writeDataToFile(temporaryFolder.newFile("test.jpg"), "1234"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.imagesSize).isEqualTo(4); + } + + @Test + public void testVideoFile() throws Exception { + writeDataToFile(temporaryFolder.newFile("test.mp4"), "1234"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.videosSize).isEqualTo(4); + } + + @Test + public void testAudioFile() throws Exception { + writeDataToFile(temporaryFolder.newFile("test.mp3"), "1234"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.audioSize).isEqualTo(4); + } + + @Test + public void testMiscFile() throws Exception { + writeDataToFile(temporaryFolder.newFile("test"), "1234"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.miscSize).isEqualTo(4); + } + + @Test + public void testNestedFile() throws Exception { + File directory = temporaryFolder.newFolder(); + writeDataToFile(new File(directory, "test"), "1234"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.miscSize).isEqualTo(4); + } + + @Test + public void testMultipleFiles() throws Exception { + writeDataToFile(temporaryFolder.newFile("test"), "1234"); + writeDataToFile(temporaryFolder.newFile("test2"), "12345"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.miscSize).isEqualTo(9); + } + + @Test + public void testTotalSize() throws Exception { + writeDataToFile(temporaryFolder.newFile("test.jpg"), "1"); + writeDataToFile(temporaryFolder.newFile("test.mp3"), "1"); + writeDataToFile(temporaryFolder.newFile("test.mp4"), "1"); + writeDataToFile(temporaryFolder.newFile("test"), "1"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.totalAccountedSize()).isEqualTo(4); + } + + @Test + public void testFileEndsWithPeriod() throws Exception { + writeDataToFile(temporaryFolder.newFile("test."), "1"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.miscSize).isEqualTo(1); + assertThat(result.totalAccountedSize()).isEqualTo(1); + } + + public void testIgnoreFileExtensionCase() throws Exception { + writeDataToFile(temporaryFolder.newFile("test.JpG"), "1234"); + + MeasurementResult result = FileCollector.getMeasurementResult(temporaryFolder.getRoot()); + + assertThat(result.imagesSize).isEqualTo(4); + } + + private void writeDataToFile(File f, String data) throws Exception{ + PrintStream out = new PrintStream(f); + out.print(data); + out.close(); + } +} |