summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteve Howard <showard@google.com>2010-09-16 12:04:17 -0700
committerSteve Howard <showard@google.com>2010-09-20 13:45:30 -0700
commitb9a0ad7182209d4aca708e13e876e9b1b43ffafc (patch)
treee49d39e80dfc9c2ffc6a43c81f08ee0edce26c31 /src
parentdbefa6f5eff88f97dd91a8adfd65dbd946adb99e (diff)
downloadandroid_packages_providers_DownloadProvider-b9a0ad7182209d4aca708e13e876e9b1b43ffafc.tar.gz
android_packages_providers_DownloadProvider-b9a0ad7182209d4aca708e13e876e9b1b43ffafc.tar.bz2
android_packages_providers_DownloadProvider-b9a0ad7182209d4aca708e13e876e9b1b43ffafc.zip
Improve file error reporting + new detailed error messages in UI
* support new error code for "destination file already exists" * improve error handling for various file error cases to return a more specific error code when appropriate * make UI support more detailed error messages for some cases * use Uri.getPath() instead of Uri.getSchemeSpecificPart() for file URIs Change-Id: Icb01d4d3b47c7776be3ddcd8347212e950cd023e
Diffstat (limited to 'src')
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java8
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java15
-rw-r--r--src/com/android/providers/downloads/Helpers.java147
3 files changed, 103 insertions, 67 deletions
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index df7ca71d..102c611d 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -121,6 +121,7 @@ public final class DownloadProvider extends ContentProvider {
Downloads.Impl.COLUMN_DESCRIPTION,
Downloads.Impl.COLUMN_URI,
Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
+ Downloads.Impl.COLUMN_FILE_NAME_HINT,
};
private static HashSet<String> sAppReadableColumnsSet;
@@ -508,9 +509,12 @@ public final class DownloadProvider extends ContentProvider {
if (!uri.getScheme().equals("file")) {
throw new IllegalArgumentException("Not a file URI: " + uri);
}
- File path = new File(uri.getSchemeSpecificPart());
+ String path = uri.getPath();
+ if (path == null) {
+ throw new IllegalArgumentException("Invalid file URI: " + uri);
+ }
String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath();
- if (!path.getPath().startsWith(externalPath)) {
+ if (!path.startsWith(externalPath)) {
throw new SecurityException("Destination must be on external storage: " + uri);
}
}
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index b2353e16..57007f49 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -426,9 +426,18 @@ public class DownloadThread extends Thread {
}
return;
} catch (IOException ex) {
- if (mInfo.isOnCache()
- && Helpers.discardPurgeableFiles(mContext, Constants.BUFFER_SIZE)) {
- continue;
+ if (mInfo.isOnCache()) {
+ if (Helpers.discardPurgeableFiles(mContext, Constants.BUFFER_SIZE)) {
+ continue;
+ }
+ } else if (!Helpers.isExternalMediaMounted()) {
+ throw new StopRequest(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR);
+ }
+
+ long availableBytes =
+ Helpers.getAvailableBytes(Helpers.getFilesystemRoot(state.mFilename));
+ if (availableBytes < bytesRead) {
+ throw new StopRequest(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR, ex);
}
throw new StopRequest(Downloads.Impl.STATUS_FILE_ERROR, ex);
}
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java
index f8900d9a..f1f23b17 100644
--- a/src/com/android/providers/downloads/Helpers.java
+++ b/src/com/android/providers/downloads/Helpers.java
@@ -104,7 +104,7 @@ public class Helpers {
String fullFilename;
try {
if (destination == Downloads.Impl.DESTINATION_FILE_URI) {
- fullFilename = getPathForFileUri(hint);
+ fullFilename = getPathForFileUri(hint, contentLength);
} else {
fullFilename = chooseFullPath(context, url, hint, contentDisposition,
contentLocation, mimeType, destination,
@@ -117,16 +117,38 @@ public class Helpers {
return new DownloadFileInfo(fullFilename, new FileOutputStream(fullFilename), 0);
}
- private static String getPathForFileUri(String hint) throws GenerateSaveFileError {
- String path = Uri.parse(hint).getSchemeSpecificPart();
+ private static String getPathForFileUri(String hint, long contentLength)
+ throws GenerateSaveFileError {
+ if (!isExternalMediaMounted()) {
+ throw new GenerateSaveFileError(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR);
+ }
+ String path = Uri.parse(hint).getPath();
if (new File(path).exists()) {
Log.d(Constants.TAG, "File already exists: " + path);
- throw new GenerateSaveFileError(Downloads.Impl.STATUS_FILE_ERROR);
+ throw new GenerateSaveFileError(Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR);
+ }
+ if (getAvailableBytes(getFilesystemRoot(path)) < contentLength) {
+ throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR);
}
return path;
}
+ /**
+ * @return the root of the filesystem containing the given path
+ */
+ public static File getFilesystemRoot(String path) {
+ File cache = Environment.getDownloadCacheDirectory();
+ if (path.startsWith(cache.getPath())) {
+ return cache;
+ }
+ File external = Environment.getExternalStorageDirectory();
+ if (path.startsWith(external.getPath())) {
+ return external;
+ }
+ throw new IllegalArgumentException("Cannot determine filesystem root for " + path);
+ }
+
private static String chooseFullPath(Context context, String url, String hint,
String contentDisposition, String contentLocation,
String mimeType, int destination, long contentLength)
@@ -203,77 +225,78 @@ public class Helpers {
private static File locateDestinationDirectory(Context context, String mimeType,
int destination, long contentLength)
throws GenerateSaveFileError {
- File base = null;
- StatFs stat = null;
// DRM messages should be temporarily stored internally and then passed to
// the DRM content provider
if (destination == Downloads.Impl.DESTINATION_CACHE_PARTITION
|| destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
|| destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING
|| DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) {
- // Saving to internal storage.
- base = Environment.getDownloadCacheDirectory();
- stat = new StatFs(base.getPath());
-
- /*
- * Check whether there's enough space on the target filesystem to save the file.
- * Put a bit of margin (in case creating the file grows the system by a few blocks).
- */
- int blockSize = stat.getBlockSize();
- long bytesAvailable = blockSize * ((long) stat.getAvailableBlocks() - 4);
- while (bytesAvailable < contentLength) {
- // Insufficient space; try discarding purgeable files.
- if (!discardPurgeableFiles(context, contentLength - bytesAvailable)) {
- // No files to purge, give up.
- if (Config.LOGD) {
- Log.d(Constants.TAG,
- "download aborted - not enough free space in internal storage");
- }
- throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR);
- } else {
- // Recalculate available space and try again.
- stat.restat(base.getPath());
- bytesAvailable = blockSize * ((long) stat.getAvailableBlocks() - 4);
- }
- }
- } else if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- // Saving to external storage (SD card).
- String root = Environment.getExternalStorageDirectory().getPath();
- stat = new StatFs(root);
-
- /*
- * Check whether there's enough space on the target filesystem to save the file.
- * Put a bit of margin (in case creating the file grows the system by a few blocks).
- */
- if (stat.getBlockSize() * ((long) stat.getAvailableBlocks() - 4) < contentLength) {
- // Insufficient space.
- if (Config.LOGD) {
- Log.d(Constants.TAG, "download aborted - not enough free space");
- }
- throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR);
- }
+ return getCacheDestination(context, contentLength);
+ }
- base = new File(root + Constants.DEFAULT_DL_SUBDIR);
- if (!base.isDirectory() && !base.mkdir()) {
- // Can't create download directory, e.g. because a file called "download"
- // already exists at the root level, or the SD card filesystem is read-only.
- if (Config.LOGD) {
- Log.d(Constants.TAG, "download aborted - can't create base directory "
- + base.getPath());
- }
- throw new GenerateSaveFileError(Downloads.Impl.STATUS_FILE_ERROR);
- }
- } else {
- // No SD card found.
- if (Config.LOGD) {
- Log.d(Constants.TAG, "download aborted - no external storage");
- }
+ return getExternalDestination(contentLength);
+ }
+
+ private static File getExternalDestination(long contentLength) throws GenerateSaveFileError {
+ if (!isExternalMediaMounted()) {
throw new GenerateSaveFileError(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR);
}
+ File root = Environment.getExternalStorageDirectory();
+ if (getAvailableBytes(root) < contentLength) {
+ // Insufficient space.
+ Log.d(Constants.TAG, "download aborted - not enough free space");
+ throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR);
+ }
+
+ File base = new File(root.getPath() + Constants.DEFAULT_DL_SUBDIR);
+ if (!base.isDirectory() && !base.mkdir()) {
+ // Can't create download directory, e.g. because a file called "download"
+ // already exists at the root level, or the SD card filesystem is read-only.
+ Log.d(Constants.TAG, "download aborted - can't create base directory "
+ + base.getPath());
+ throw new GenerateSaveFileError(Downloads.Impl.STATUS_FILE_ERROR);
+ }
return base;
}
+ public static boolean isExternalMediaMounted() {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ // No SD card found.
+ Log.d(Constants.TAG, "no external storage");
+ return false;
+ }
+ return true;
+ }
+
+ private static File getCacheDestination(Context context, long contentLength)
+ throws GenerateSaveFileError {
+ File base;
+ base = Environment.getDownloadCacheDirectory();
+ long bytesAvailable = getAvailableBytes(base);
+ while (bytesAvailable < contentLength) {
+ // Insufficient space; try discarding purgeable files.
+ if (!discardPurgeableFiles(context, contentLength - bytesAvailable)) {
+ // No files to purge, give up.
+ Log.d(Constants.TAG,
+ "download aborted - not enough free space in internal storage");
+ throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR);
+ }
+ bytesAvailable = getAvailableBytes(base);
+ }
+ return base;
+ }
+
+ /**
+ * @return the number of bytes available on the filesystem rooted at the given File
+ */
+ public static long getAvailableBytes(File root) {
+ StatFs stat = new StatFs(root.getPath());
+ // put a bit of margin (in case creating the file grows the system by a few blocks)
+ long availableBlocks = (long) stat.getAvailableBlocks() - 4;
+ return stat.getBlockSize() * availableBlocks;
+ }
+
private static String chooseFilename(String url, String hint, String contentDisposition,
String contentLocation, int destination) {
String filename = null;