From 97086b259248d3d46a0d558aff8a32d1c6987777 Mon Sep 17 00:00:00 2001 From: James Sullins Date: Fri, 5 Sep 2014 12:10:28 -0500 Subject: DownloadProvider: fix secondary storage support Change-Id: If9aba4c73b24d052b0379d624302ffda03fdf8e4 --- .../providers/downloads/DownloadProvider.java | 29 ++++--- .../providers/downloads/DownloadThread.java | 5 +- src/com/android/providers/downloads/Helpers.java | 24 +++--- .../providers/downloads/StorageManager.java | 94 ++++++++++------------ 4 files changed, 75 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java index ed793aa6..25d59014 100644 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ b/src/com/android/providers/downloads/DownloadProvider.java @@ -710,15 +710,26 @@ public final class DownloadProvider extends ContentProvider { if (path == null) { throw new IllegalArgumentException("Invalid file URI: " + uri); } - - final String phoneStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - String sdCardStoragePath = null; - if (StorageManager.isSecondStorageSupported(getContext())) { - sdCardStoragePath = StorageManager.getExternalStorageDirectory(getContext()); - } - if (!path.startsWith(phoneStoragePath) - && !(sdCardStoragePath != null && path.startsWith(sdCardStoragePath))) { - throw new SecurityException("Destination must be on external storage: " + uri); + try { + boolean isValidExternalPath = false; + final String canonicalPath = new File(path).getCanonicalPath(); + final String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath(); + final List secondaryPaths = StorageManager.getSecondaryStoragePaths(getContext()); + if (canonicalPath.startsWith(externalPath)) { + isValidExternalPath = true; + } else { + for (String secondaryPath : secondaryPaths) { + if (canonicalPath.startsWith(secondaryPath)) { + isValidExternalPath = true; + break; + } + } + } + if (!isValidExternalPath) { + throw new SecurityException("Destination must be on external storage: " + uri); + } + } catch (IOException e) { + throw new SecurityException("Problem resolving path: " + uri); } } diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 517d8f8f..88cd9162 100755 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -583,7 +583,7 @@ public class DownloadThread implements Runnable { */ private void writeDataToDestination(State state, byte[] data, int bytesRead, OutputStream out) throws StopRequestException { - mStorageManager.verifySpaceBeforeWritingToFile(mContext, + mStorageManager.verifySpaceBeforeWritingToFile( mInfo.mDestination, state.mFilename, bytesRead); boolean forceVerified = false; @@ -595,8 +595,7 @@ public class DownloadThread implements Runnable { // TODO: better differentiate between DRM and disk failures if (!forceVerified) { // couldn't write to file. are we out of space? check. - mStorageManager.verifySpace(mContext, mInfo.mDestination, - state.mFilename, bytesRead); + mStorageManager.verifySpace(mInfo.mDestination, state.mFilename, bytesRead); forceVerified = true; } else { throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java index 0739eea2..40cf5e30 100644 --- a/src/com/android/providers/downloads/Helpers.java +++ b/src/com/android/providers/downloads/Helpers.java @@ -28,6 +28,7 @@ import android.webkit.MimeTypeMap; import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; @@ -92,7 +93,7 @@ public class Helpers { path = chooseFilename(url, hint, contentDisposition, contentLocation, destination); } - storageManager.verifySpace(context, destination, path, contentLength); + storageManager.verifySpace(destination, path, contentLength); if (DownloadDrmHelper.isDrmConvertNeeded(mimeType)) { path = DownloadDrmHelper.modifyDrmFwLockFileExtension(path); } @@ -351,8 +352,9 @@ public class Helpers { /** * Checks whether the filename looks legitimate */ - static boolean isFilenameValid(String filename, File downloadsDataDir) { + static boolean isFilenameValid(Context context, String filename, File downloadsDataDir) { final String[] whitelist; + final List secondaryStoragePaths = StorageManager.getSecondaryStoragePaths(context); try { filename = new File(filename).getCanonicalPath(); whitelist = new String[] { @@ -371,19 +373,13 @@ public class Helpers { } } - return false; - } + for (String test : secondaryStoragePaths) { + if (filename.startsWith(test)) { + return true; + } + } - /** - * Checks whether the filename looks legitimate - */ - static boolean isFilenameValid(Context context, String filename, File downloadsDataDir) { - filename = filename.replaceFirst("/+", "/"); // normalize leading slashes - return filename.startsWith(Environment.getDownloadCacheDirectory().toString()) - || filename.startsWith(downloadsDataDir.toString()) - || filename.startsWith(Environment.getExternalStorageDirectory().toString()) - || (StorageManager.isSecondStorageSupported(context) - && filename.startsWith(StorageManager.getExternalStorageDirectory(context))); + return false; } /** diff --git a/src/com/android/providers/downloads/StorageManager.java b/src/com/android/providers/downloads/StorageManager.java index 4194ec8b..41592f4a 100644 --- a/src/com/android/providers/downloads/StorageManager.java +++ b/src/com/android/providers/downloads/StorageManager.java @@ -19,7 +19,6 @@ package com.android.providers.downloads; import static com.android.providers.downloads.Constants.LOGV; import static com.android.providers.downloads.Constants.TAG; -import android.app.Activity; import android.content.ContentUris; import android.content.Context; import android.content.res.Resources; @@ -65,6 +64,9 @@ class StorageManager { /** see {@link Environment#getExternalStorageDirectory()} */ private final File mExternalStorageDir; + /** list of secondary external storage paths */ + private final List mSecondaryStoragePaths; + /** see {@link Environment#getDownloadCacheDirectory()} */ private final File mSystemCacheDir; @@ -79,20 +81,14 @@ class StorageManager { /** misc members */ private final Context mContext; - private static String sdCardStorageDir; - private static final String LOGTAG = "StorageManager"; public StorageManager(Context context) { mContext = context; mDownloadDataDir = getDownloadDataDirectory(context); mExternalStorageDir = Environment.getExternalStorageDirectory(); mSystemCacheDir = Environment.getDownloadCacheDirectory(); + mSecondaryStoragePaths = getSecondaryStoragePaths(context); startThreadToCleanupDatabaseAndPurgeFileSystem(); - if (isSecondStorageSupported(context)) { - sdCardStorageDir = getExternalStorageDirectory(context); - } else { - sdCardStorageDir = null; - } } /** How often should database and filesystem be cleaned up to remove spurious files @@ -125,20 +121,20 @@ class StorageManager { mCleanupThread.start(); } - void verifySpaceBeforeWritingToFile(Context context, int destination, String path, long length) + void verifySpaceBeforeWritingToFile(int destination, String path, long length) throws StopRequestException { // do this check only once for every 1MB of downloaded data if (incrementBytesDownloadedSinceLastCheckOnSpace(length) < FREQUENCY_OF_CHECKS_ON_SPACE_AVAILABILITY) { return; } - verifySpace(context, destination, path, length); + verifySpace(destination, path, length); } - void verifySpace(Context context, int destination, String path, - long length) throws StopRequestException { + void verifySpace(int destination, String path, long length) throws StopRequestException { resetBytesDownloadedSinceLastCheckOnSpace(); File dir = null; + boolean isSecondaryStorage = false; if (Constants.LOGV) { Log.i(Constants.TAG, "in verifySpace, destination: " + destination + ", path: " + path + ", length: " + length); @@ -159,14 +155,25 @@ class StorageManager { dir = mSystemCacheDir; break; case Downloads.Impl.DESTINATION_FILE_URI: - if (isSecondStorageSupported(context) && path.startsWith(sdCardStorageDir)) { - dir = new File(sdCardStorageDir); - } else if (path.startsWith(mExternalStorageDir.getPath())) { + if (path.startsWith(mExternalStorageDir.getPath())) { dir = mExternalStorageDir; - } else if (path.startsWith(mDownloadDataDir.getPath())) { - dir = mDownloadDataDir; - } else if (path.startsWith(mSystemCacheDir.getPath())) { - dir = mSystemCacheDir; + } else { + for (String secondaryPath : mSecondaryStoragePaths) { + if (path.startsWith(secondaryPath)) { + isSecondaryStorage = true; + dir = new File(secondaryPath); + break; + } + } + } + // TODO - should only check external storage? + if (dir == null) { + if (path.startsWith(mDownloadDataDir.getPath())) { + dir = mDownloadDataDir; + } + else if (path.startsWith(mSystemCacheDir.getPath())) { + dir = mSystemCacheDir; + } } break; } @@ -174,7 +181,7 @@ class StorageManager { throw new IllegalStateException("invalid combination of destination: " + destination + ", path: " + path); } - findSpace(context, dir, length, destination); + findSpace(dir, length, destination, isSecondaryStorage); } /** @@ -182,18 +189,20 @@ class StorageManager { * specified by the input param(targetBytes). * returns true if found. false otherwise. */ - private synchronized void findSpace(Context context, File root, long targetBytes, - int destination) throws StopRequestException { + private synchronized void findSpace(File root, long targetBytes, int destination, + boolean isSecondaryStorage) throws StopRequestException { if (targetBytes == 0) { return; } - if (!(isSecondStorageSupported(context) && root.getPath().startsWith(sdCardStorageDir))) { - if (destination == Downloads.Impl.DESTINATION_FILE_URI || - destination == Downloads.Impl.DESTINATION_EXTERNAL) { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new StopRequestException(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR, - "external media not mounted"); - } + if (root == mExternalStorageDir) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new StopRequestException(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR, + "external media not mounted"); + } + } else if (isSecondaryStorage) { + if (!Environment.getStorageState(root).equals(Environment.MEDIA_MOUNTED)) { + throw new StopRequestException(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR, + "external media not mounted"); } } // is there enough space in the file system of the given param 'root'. @@ -484,35 +493,18 @@ class StorageManager { mBytesDownloadedSinceLastCheckOnSpace = 0; } - /** - * Whether the device supports second storage - * @return boolean true support second storage, false does not - */ - public static boolean isSecondStorageSupported(Context context) { + public static List getSecondaryStoragePaths(Context context) { + List secondaryPaths = new ArrayList(); android.os.storage.StorageManager storageManager = (android.os.storage.StorageManager) context .getSystemService(Context.STORAGE_SERVICE); StorageVolume[] volumes = storageManager.getVolumeList(); for (int i = 0; i < volumes.length; i++) { - if (!volumes[i].isPrimary() && volumes[i].allowMassStorage()) { - return true; + if (!volumes[i].isPrimary()) { + secondaryPaths.add(volumes[i].getPath()); } } - return false; + return secondaryPaths; } - public static String getExternalStorageDirectory(Context context) { - String sdCardDir = null; - android.os.storage.StorageManager storageManager = - (android.os.storage.StorageManager) context - .getSystemService(Context.STORAGE_SERVICE); - StorageVolume[] volumes = storageManager.getVolumeList(); - for (int i = 0; i < volumes.length; i++) { - if (volumes[i].isRemovable() && volumes[i].allowMassStorage()) { - sdCardDir = volumes[i].getPath(); - break; - } - } - return sdCardDir; - } } -- cgit v1.2.3