Loading core/java/android/os/FileUtils.java +75 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,13 @@ package android.os; import android.provider.DocumentsContract.Document; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.webkit.MimeTypeMap; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; Loading @@ -34,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; Loading Loading @@ -533,4 +536,76 @@ public class FileUtils { } return null; } /** * Generates a unique file name under the given parent directory. If the display name doesn't * have an extension that matches the requested MIME type, the default extension for that MIME * type is appended. If a file already exists, the name is appended with a numerical value to * make it unique. * * For example, the display name 'example' with 'text/plain' MIME might produce * 'example.txt' or 'example (1).txt', etc. * * @throws FileNotFoundException */ public static File buildUniqueFile(File parent, String mimeType, String displayName) throws FileNotFoundException { String name; String ext; if (Document.MIME_TYPE_DIR.equals(mimeType)) { name = displayName; ext = null; } else { String mimeTypeFromExt; // Extract requested extension from display name final int lastDot = displayName.lastIndexOf('.'); if (lastDot >= 0) { name = displayName.substring(0, lastDot); ext = displayName.substring(lastDot + 1); mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( ext.toLowerCase()); } else { name = displayName; ext = null; mimeTypeFromExt = null; } if (mimeTypeFromExt == null) { mimeTypeFromExt = "application/octet-stream"; } final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( mimeType); if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { // Extension maps back to requested MIME type; allow it } else { // No match; insist that create file matches requested MIME name = displayName; ext = extFromMimeType; } } File file = buildFile(parent, name, ext); // If conflicting file, try adding counter suffix int n = 0; while (file.exists()) { if (n++ >= 32) { throw new FileNotFoundException("Failed to create unique file"); } file = buildFile(parent, name + " (" + n + ")", ext); } return file; } private static File buildFile(File parent, String name, String ext) { if (TextUtils.isEmpty(ext)) { return new File(parent, name); } else { return new File(parent, name + "." + ext); } } } core/tests/coretests/src/android/os/FileUtilsTest.java +64 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import android.content.Context; import android.provider.DocumentsContract.Document; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; Loading @@ -43,6 +44,8 @@ public class FileUtilsTest extends AndroidTestCase { private File mDir; private File mTestFile; private File mCopyFile; private File mTarget; @Override protected void setUp() throws Exception { Loading @@ -50,11 +53,15 @@ public class FileUtilsTest extends AndroidTestCase { mDir = getContext().getDir("testing", Context.MODE_PRIVATE); mTestFile = new File(mDir, "test.file"); mCopyFile = new File(mDir, "copy.file"); mTarget = getContext().getFilesDir(); FileUtils.deleteContents(mTarget); } @Override protected void tearDown() throws Exception { IoUtils.deleteContents(mDir); FileUtils.deleteContents(mTarget); } // TODO: test setPermissions(), getPermissions() Loading Loading @@ -225,6 +232,63 @@ public class FileUtilsTest extends AndroidTestCase { assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz")); } public void testBuildUniqueFile_normal() throws Exception { assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg")); assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg")); assertNameEquals("test.png.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg")); assertNameEquals("test.png.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac")); } public void testBuildUniqueFile_unknown() throws Exception { assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg")); assertNameEquals(".test", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test")); assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test")); assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz")); } public void testBuildUniqueFile_dir() throws Exception { assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test")); new File(mTarget, "test").mkdir(); assertNameEquals("test (1)", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg")); new File(mTarget, "test.jpg").mkdir(); assertNameEquals("test.jpg (1)", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg")); } public void testBuildUniqueFile_increment() throws Exception { assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); new File(mTarget, "test.jpg").createNewFile(); assertNameEquals("test (1).jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); new File(mTarget, "test (1).jpg").createNewFile(); assertNameEquals("test (2).jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); } private static void assertNameEquals(String expected, File actual) { assertEquals(expected, actual.getName()); } private void touch(String name, long age) throws Exception { final File file = new File(mDir, name); file.createNewFile(); Loading packages/DocumentsUI/Android.mk +2 −0 Original line number Diff line number Diff line Loading @@ -11,3 +11,5 @@ LOCAL_PACKAGE_NAME := DocumentsUI LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) include $(LOCAL_PATH)/tests/Android.mk packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +1 −65 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import android.util.Log; import android.webkit.MimeTypeMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.File; Loading @@ -55,7 +54,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.LinkedList; import java.util.List; import java.util.Objects; public class ExternalStorageProvider extends DocumentsProvider { private static final String TAG = "ExternalStorage"; Loading Loading @@ -327,7 +325,7 @@ public class ExternalStorageProvider extends DocumentsProvider { throw new IllegalArgumentException("Parent document isn't a directory"); } final File file = buildUniqueFile(parent, mimeType, displayName); final File file = FileUtils.buildUniqueFile(parent, mimeType, displayName); if (Document.MIME_TYPE_DIR.equals(mimeType)) { if (!file.mkdir()) { throw new IllegalStateException("Failed to mkdir " + file); Loading @@ -345,68 +343,6 @@ public class ExternalStorageProvider extends DocumentsProvider { return getDocIdForFile(file); } private static File buildFile(File parent, String name, String ext) { if (TextUtils.isEmpty(ext)) { return new File(parent, name); } else { return new File(parent, name + "." + ext); } } @VisibleForTesting public static File buildUniqueFile(File parent, String mimeType, String displayName) throws FileNotFoundException { String name; String ext; if (Document.MIME_TYPE_DIR.equals(mimeType)) { name = displayName; ext = null; } else { String mimeTypeFromExt; // Extract requested extension from display name final int lastDot = displayName.lastIndexOf('.'); if (lastDot >= 0) { name = displayName.substring(0, lastDot); ext = displayName.substring(lastDot + 1); mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( ext.toLowerCase()); } else { name = displayName; ext = null; mimeTypeFromExt = null; } if (mimeTypeFromExt == null) { mimeTypeFromExt = "application/octet-stream"; } final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( mimeType); if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { // Extension maps back to requested MIME type; allow it } else { // No match; insist that create file matches requested MIME name = displayName; ext = extFromMimeType; } } File file = buildFile(parent, name, ext); // If conflicting file, try adding counter suffix int n = 0; while (file.exists()) { if (n++ >= 32) { throw new FileNotFoundException("Failed to create unique file"); } file = buildFile(parent, name + " (" + n + ")", ext); } return file; } @Override public String renameDocument(String docId, String displayName) throws FileNotFoundException { // Since this provider treats renames as generating a completely new Loading packages/ExternalStorageProvider/tests/Android.mkdeleted 100644 → 0 +0 −16 Original line number Diff line number Diff line LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := ExternalStorageProviderTests LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) Loading
core/java/android/os/FileUtils.java +75 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,13 @@ package android.os; import android.provider.DocumentsContract.Document; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.webkit.MimeTypeMap; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; Loading @@ -34,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; Loading Loading @@ -533,4 +536,76 @@ public class FileUtils { } return null; } /** * Generates a unique file name under the given parent directory. If the display name doesn't * have an extension that matches the requested MIME type, the default extension for that MIME * type is appended. If a file already exists, the name is appended with a numerical value to * make it unique. * * For example, the display name 'example' with 'text/plain' MIME might produce * 'example.txt' or 'example (1).txt', etc. * * @throws FileNotFoundException */ public static File buildUniqueFile(File parent, String mimeType, String displayName) throws FileNotFoundException { String name; String ext; if (Document.MIME_TYPE_DIR.equals(mimeType)) { name = displayName; ext = null; } else { String mimeTypeFromExt; // Extract requested extension from display name final int lastDot = displayName.lastIndexOf('.'); if (lastDot >= 0) { name = displayName.substring(0, lastDot); ext = displayName.substring(lastDot + 1); mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( ext.toLowerCase()); } else { name = displayName; ext = null; mimeTypeFromExt = null; } if (mimeTypeFromExt == null) { mimeTypeFromExt = "application/octet-stream"; } final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( mimeType); if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { // Extension maps back to requested MIME type; allow it } else { // No match; insist that create file matches requested MIME name = displayName; ext = extFromMimeType; } } File file = buildFile(parent, name, ext); // If conflicting file, try adding counter suffix int n = 0; while (file.exists()) { if (n++ >= 32) { throw new FileNotFoundException("Failed to create unique file"); } file = buildFile(parent, name + " (" + n + ")", ext); } return file; } private static File buildFile(File parent, String name, String ext) { if (TextUtils.isEmpty(ext)) { return new File(parent, name); } else { return new File(parent, name + "." + ext); } } }
core/tests/coretests/src/android/os/FileUtilsTest.java +64 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import android.content.Context; import android.provider.DocumentsContract.Document; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; Loading @@ -43,6 +44,8 @@ public class FileUtilsTest extends AndroidTestCase { private File mDir; private File mTestFile; private File mCopyFile; private File mTarget; @Override protected void setUp() throws Exception { Loading @@ -50,11 +53,15 @@ public class FileUtilsTest extends AndroidTestCase { mDir = getContext().getDir("testing", Context.MODE_PRIVATE); mTestFile = new File(mDir, "test.file"); mCopyFile = new File(mDir, "copy.file"); mTarget = getContext().getFilesDir(); FileUtils.deleteContents(mTarget); } @Override protected void tearDown() throws Exception { IoUtils.deleteContents(mDir); FileUtils.deleteContents(mTarget); } // TODO: test setPermissions(), getPermissions() Loading Loading @@ -225,6 +232,63 @@ public class FileUtilsTest extends AndroidTestCase { assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz")); } public void testBuildUniqueFile_normal() throws Exception { assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg")); assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg")); assertNameEquals("test.png.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg")); assertNameEquals("test.png.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test")); assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac")); } public void testBuildUniqueFile_unknown() throws Exception { assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg")); assertNameEquals(".test", FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test")); assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test")); assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz")); } public void testBuildUniqueFile_dir() throws Exception { assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test")); new File(mTarget, "test").mkdir(); assertNameEquals("test (1)", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg")); new File(mTarget, "test.jpg").mkdir(); assertNameEquals("test.jpg (1)", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg")); } public void testBuildUniqueFile_increment() throws Exception { assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); new File(mTarget, "test.jpg").createNewFile(); assertNameEquals("test (1).jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); new File(mTarget, "test (1).jpg").createNewFile(); assertNameEquals("test (2).jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); } private static void assertNameEquals(String expected, File actual) { assertEquals(expected, actual.getName()); } private void touch(String name, long age) throws Exception { final File file = new File(mDir, name); file.createNewFile(); Loading
packages/DocumentsUI/Android.mk +2 −0 Original line number Diff line number Diff line Loading @@ -11,3 +11,5 @@ LOCAL_PACKAGE_NAME := DocumentsUI LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) include $(LOCAL_PATH)/tests/Android.mk
packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +1 −65 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import android.util.Log; import android.webkit.MimeTypeMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.File; Loading @@ -55,7 +54,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.LinkedList; import java.util.List; import java.util.Objects; public class ExternalStorageProvider extends DocumentsProvider { private static final String TAG = "ExternalStorage"; Loading Loading @@ -327,7 +325,7 @@ public class ExternalStorageProvider extends DocumentsProvider { throw new IllegalArgumentException("Parent document isn't a directory"); } final File file = buildUniqueFile(parent, mimeType, displayName); final File file = FileUtils.buildUniqueFile(parent, mimeType, displayName); if (Document.MIME_TYPE_DIR.equals(mimeType)) { if (!file.mkdir()) { throw new IllegalStateException("Failed to mkdir " + file); Loading @@ -345,68 +343,6 @@ public class ExternalStorageProvider extends DocumentsProvider { return getDocIdForFile(file); } private static File buildFile(File parent, String name, String ext) { if (TextUtils.isEmpty(ext)) { return new File(parent, name); } else { return new File(parent, name + "." + ext); } } @VisibleForTesting public static File buildUniqueFile(File parent, String mimeType, String displayName) throws FileNotFoundException { String name; String ext; if (Document.MIME_TYPE_DIR.equals(mimeType)) { name = displayName; ext = null; } else { String mimeTypeFromExt; // Extract requested extension from display name final int lastDot = displayName.lastIndexOf('.'); if (lastDot >= 0) { name = displayName.substring(0, lastDot); ext = displayName.substring(lastDot + 1); mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( ext.toLowerCase()); } else { name = displayName; ext = null; mimeTypeFromExt = null; } if (mimeTypeFromExt == null) { mimeTypeFromExt = "application/octet-stream"; } final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( mimeType); if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { // Extension maps back to requested MIME type; allow it } else { // No match; insist that create file matches requested MIME name = displayName; ext = extFromMimeType; } } File file = buildFile(parent, name, ext); // If conflicting file, try adding counter suffix int n = 0; while (file.exists()) { if (n++ >= 32) { throw new FileNotFoundException("Failed to create unique file"); } file = buildFile(parent, name + " (" + n + ")", ext); } return file; } @Override public String renameDocument(String docId, String displayName) throws FileNotFoundException { // Since this provider treats renames as generating a completely new Loading
packages/ExternalStorageProvider/tests/Android.mkdeleted 100644 → 0 +0 −16 Original line number Diff line number Diff line LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := ExternalStorageProviderTests LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE)