Commit b9456c16 authored by Xing Liu's avatar Xing Liu Committed by Commit Bot

Download location: Fix issue that can't access file on SD card.

Chrome uses file URL to access file on disk. The path needs to be in
a directory white list.

This CL adds the external SD card directory to the white list, so file
can be accessed through file URL. Also moves the core function
getAllDownloadDirectories to PathUtils for easier access in native code.


Bug: 838419,792775
Change-Id: If52202d6a62f89106b1e8533f8c1884b17cc9e0b
Reviewed-on: https://chromium-review.googlesource.com/1050394
Commit-Queue: Xing Liu <xingliu@chromium.org>
Reviewed-by: default avataragrieve <agrieve@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarShakti Sahu <shaktisahu@chromium.org>
Reviewed-by: default avatarMaks Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#557759}
parent d5efb3fb
...@@ -210,6 +210,27 @@ public abstract class PathUtils { ...@@ -210,6 +210,27 @@ public abstract class PathUtils {
} }
} }
/**
* @return Download directories including the default storage directory on SD card, and a
* private directory on external SD card.
*/
@SuppressWarnings("unused")
@CalledByNative
public static String[] getAllPrivateDownloadsDirectories() {
File[] files;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
files = ContextUtils.getApplicationContext().getExternalFilesDirs(
Environment.DIRECTORY_DOWNLOADS);
} else {
files = new File[] {Environment.getExternalStorageDirectory()};
}
String[] result = new String[files.length];
for (int i = 0; i < files.length; ++i) {
result[i] = files[i].getAbsolutePath();
}
return result;
}
/** /**
* @return the path to native libraries. * @return the path to native libraries.
*/ */
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "base/android/path_utils.h" #include "base/android/path_utils.h"
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h" #include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h" #include "base/android/scoped_java_ref.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
...@@ -47,6 +48,18 @@ bool GetDownloadsDirectory(FilePath* result) { ...@@ -47,6 +48,18 @@ bool GetDownloadsDirectory(FilePath* result) {
return true; return true;
} }
std::vector<FilePath> GetAllPrivateDownloadsDirectories() {
std::vector<std::string> dirs;
JNIEnv* env = AttachCurrentThread();
auto jarray = Java_PathUtils_getAllPrivateDownloadsDirectories(env);
base::android::AppendJavaStringArrayToStringVector(env, jarray.obj(), &dirs);
std::vector<base::FilePath> file_paths;
for (const auto& dir : dirs)
file_paths.emplace_back(dir);
return file_paths;
}
bool GetNativeLibraryDirectory(FilePath* result) { bool GetNativeLibraryDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path = ScopedJavaLocalRef<jstring> path =
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define BASE_ANDROID_PATH_UTILS_H_ #define BASE_ANDROID_PATH_UTILS_H_
#include <jni.h> #include <jni.h>
#include <vector>
#include "base/base_export.h" #include "base/base_export.h"
...@@ -35,6 +36,10 @@ BASE_EXPORT bool GetThumbnailCacheDirectory(FilePath* result); ...@@ -35,6 +36,10 @@ BASE_EXPORT bool GetThumbnailCacheDirectory(FilePath* result);
// in the FilePath pointed to by 'result'. // in the FilePath pointed to by 'result'.
BASE_EXPORT bool GetDownloadsDirectory(FilePath* result); BASE_EXPORT bool GetDownloadsDirectory(FilePath* result);
// Retrieves the paths to all download directories, including default storage
// directory, and a private directory on external SD card.
BASE_EXPORT std::vector<FilePath> GetAllPrivateDownloadsDirectories();
// Retrieves the path to the native JNI libraries via // Retrieves the path to the native JNI libraries via
// ApplicationInfo.nativeLibraryDir on the Java side. The result is placed in // ApplicationInfo.nativeLibraryDir on the Java side. The result is placed in
// the FilePath pointed to by 'result'. // the FilePath pointed to by 'result'.
......
...@@ -1543,10 +1543,9 @@ public class DownloadManagerService ...@@ -1543,10 +1543,9 @@ public class DownloadManagerService
return false; return false;
} }
File[] externalDirs = DownloadUtils.getAllDownloadDirectories(mContext); for (String dir : DownloadUtils.getAllDownloadDirectories()) {
for (File dir : externalDirs) { if (TextUtils.isEmpty(dir)) continue;
if (dir == null) continue; if (filePath.contains(dir)) return false;
if (filePath.contains(dir.getAbsolutePath())) return false;
} }
return true; return true;
......
...@@ -10,8 +10,6 @@ import android.content.Context; ...@@ -10,8 +10,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.StrictMode; import android.os.StrictMode;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
...@@ -23,6 +21,7 @@ import org.chromium.base.Callback; ...@@ -23,6 +21,7 @@ import org.chromium.base.Callback;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.FileUtils; import org.chromium.base.FileUtils;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.PathUtils;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
...@@ -1038,13 +1037,9 @@ public class DownloadUtils { ...@@ -1038,13 +1037,9 @@ public class DownloadUtils {
* If the external directories are not available for querying (on older versions of Android), * If the external directories are not available for querying (on older versions of Android),
* return an array with just the internal directory. * return an array with just the internal directory.
* *
* @param context Context from which to look for the directories. * @return The absolute paths of download directories.
* @return The list of directories or empty array if no directories.
*/ */
public static File[] getAllDownloadDirectories(Context context) { public static String[] getAllDownloadDirectories() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return PathUtils.getAllPrivateDownloadsDirectories();
return context.getExternalFilesDirs(Environment.DIRECTORY_DOWNLOADS);
}
return new File[] {Environment.getExternalStorageDirectory()};
} }
} }
...@@ -567,7 +567,7 @@ public class DownloadManagerUi ...@@ -567,7 +567,7 @@ public class DownloadManagerUi
private void maybeShowDownloadSettingsTextBubble(final Tracker tracker) { private void maybeShowDownloadSettingsTextBubble(final Tracker tracker) {
// If the user doesn't have an SD card don't show the IPH. // If the user doesn't have an SD card don't show the IPH.
File[] externalDirs = DownloadUtils.getAllDownloadDirectories(mActivity); String[] externalDirs = DownloadUtils.getAllDownloadDirectories();
if (externalDirs.length < 2) return; if (externalDirs.length < 2) return;
// Check to see if the help UI should be triggered. // Check to see if the help UI should be triggered.
......
...@@ -9,6 +9,7 @@ import android.os.Environment; ...@@ -9,6 +9,7 @@ import android.os.Environment;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.widget.TextViewCompat; import android.support.v4.widget.TextViewCompat;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -239,18 +240,15 @@ public class DownloadDirectoryAdapter extends ArrayAdapter<Object> { ...@@ -239,18 +240,15 @@ public class DownloadDirectoryAdapter extends ArrayAdapter<Object> {
mAdditionalOptions.clear(); mAdditionalOptions.clear();
// If there are no more additional directories, it is only the primary storage available. // If there are no more additional directories, it is only the primary storage available.
File[] externalDirs = DownloadUtils.getAllDownloadDirectories(mContext); String[] externalDirs = DownloadUtils.getAllDownloadDirectories();
if (externalDirs.length <= 1) return; if (externalDirs.length <= 1) return;
int numOtherAdditionalDirectories = 0; int numOtherAdditionalDirectories = 0;
for (File dir : externalDirs) { for (String dir : externalDirs) {
if (dir == null) continue; if (TextUtils.isEmpty(dir)) continue;
// Skip the directory that is in primary storage. // Skip the directory that is in primary storage.
if (dir.getAbsolutePath().contains( if (dir.contains(Environment.getExternalStorageDirectory().getAbsolutePath())) continue;
Environment.getExternalStorageDirectory().getAbsolutePath())) {
continue;
}
// Add index (ie. SD Card 2) if there is more than one secondary storage option. // Add index (ie. SD Card 2) if there is more than one secondary storage option.
String directoryName = (numOtherAdditionalDirectories > 0) String directoryName = (numOtherAdditionalDirectories > 0)
...@@ -259,7 +257,8 @@ public class DownloadDirectoryAdapter extends ArrayAdapter<Object> { ...@@ -259,7 +257,8 @@ public class DownloadDirectoryAdapter extends ArrayAdapter<Object> {
numOtherAdditionalDirectories + 1) numOtherAdditionalDirectories + 1)
: mContext.getString(org.chromium.chrome.R.string.downloads_location_sd_card); : mContext.getString(org.chromium.chrome.R.string.downloads_location_sd_card);
mAdditionalOptions.add(new DirectoryOption(directoryName, dir, dir.getUsableSpace())); File file = new File(dir);
mAdditionalOptions.add(new DirectoryOption(directoryName, file, file.getUsableSpace()));
numOtherAdditionalDirectories++; numOtherAdditionalDirectories++;
} }
} }
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
#include "base/android/path_utils.h"
#include "chrome/browser/io_thread.h" #include "chrome/browser/io_thread.h"
#endif #endif
...@@ -170,6 +171,10 @@ bool IsAccessAllowedInternal(const base::FilePath& path, ...@@ -170,6 +171,10 @@ bool IsAccessAllowedInternal(const base::FilePath& path,
if (external_storage_path.IsParent(path)) if (external_storage_path.IsParent(path))
return true; return true;
auto all_download_dirs = base::android::GetAllPrivateDownloadsDirectories();
for (const auto& dir : all_download_dirs)
whitelist.push_back(dir);
// Whitelist of other allowed directories. // Whitelist of other allowed directories.
static const base::FilePath::CharType* const kLocalAccessWhiteList[] = { static const base::FilePath::CharType* const kLocalAccessWhiteList[] = {
"/sdcard", "/mnt/sdcard", "/sdcard", "/mnt/sdcard",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment