Add a file collector to the platform.
The file collector takes in a file path and recursively classifies and provides the amount of storage provided by each classification. This information can be surfaced in Settings or to fulfill storage telemetry calculations. Bug: 32207207 Test: System server instrumentation tests Change-Id: Iff03260859cd4bbd11e7d60b1825115aad540d48
This commit is contained in:
218
services/core/java/com/android/server/storage/FileCollector.java
Normal file
218
services/core/java/com/android/server/storage/FileCollector.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
|
|||||||
guava \
|
guava \
|
||||||
android-support-test \
|
android-support-test \
|
||||||
mockito-target \
|
mockito-target \
|
||||||
ShortcutManagerTestUtils
|
ShortcutManagerTestUtils \
|
||||||
|
truth-prebuilt
|
||||||
|
|
||||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user