summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/android/base_jni_registrar.cc2
-rw-r--r--base/android/content_uri_utils.cc39
-rw-r--r--base/android/content_uri_utils.h27
-rw-r--r--base/android/java/src/org/chromium/base/ContentUriUtils.java76
-rw-r--r--base/base.gyp33
-rw-r--r--base/base.gypi2
-rw-r--r--base/file_util_posix.cc29
-rw-r--r--base/file_util_unittest.cc50
-rw-r--r--base/files/file_path.cc6
-rw-r--r--base/files/file_path.h9
-rw-r--r--base/files/file_path_unittest.cc29
-rw-r--r--base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java51
-rw-r--r--base/test/data/file_util/red.pngbin0 -> 173 bytes
-rw-r--r--base/test/run_all_unittests.cc10
-rw-r--r--base/test/test_file_util.h14
-rw-r--r--base/test/test_file_util_android.cc29
-rw-r--r--content/browser/child_process_security_policy_impl.cc20
-rw-r--r--net/base/file_stream_context.cc29
-rw-r--r--net/base/file_stream_unittest.cc53
-rw-r--r--net/data/file_stream_unittest/red.pngbin0 -> 173 bytes
-rw-r--r--net/net.gyp1
-rw-r--r--net/test/run_all_unittests.cc2
-rw-r--r--ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java26
23 files changed, 506 insertions, 31 deletions
diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc
index 0645c73..60ce975 100644
--- a/base/android/base_jni_registrar.cc
+++ b/base/android/base_jni_registrar.cc
@@ -6,6 +6,7 @@
#include "base/android/activity_status.h"
#include "base/android/build_info.h"
+#include "base/android/content_uri_utils.h"
#include "base/android/cpu_features.h"
#include "base/android/important_file_writer_android.h"
#include "base/android/java_handler_thread.h"
@@ -34,6 +35,7 @@ static RegistrationMethod kBaseRegisteredMethods[] = {
#if defined(GOOGLE_TV)
{ "ContextTypes", base::android::RegisterContextTypes },
#endif
+ { "ContentUriUtils", base::RegisterContentUriUtils },
{ "CpuFeatures", base::android::RegisterCpuFeatures },
{ "ImportantFileWriterAndroid",
base::android::RegisterImportantFileWriterAndroid },
diff --git a/base/android/content_uri_utils.cc b/base/android/content_uri_utils.cc
new file mode 100644
index 0000000..057106f
--- /dev/null
+++ b/base/android/content_uri_utils.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/content_uri_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/platform_file.h"
+#include "jni/ContentUriUtils_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+
+namespace base {
+
+bool RegisterContentUriUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+bool ContentUriExists(const FilePath& content_uri) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_uri =
+ ConvertUTF8ToJavaString(env, content_uri.value());
+ return Java_ContentUriUtils_contentUriExists(
+ env, base::android::GetApplicationContext(), j_uri.obj());
+}
+
+int OpenContentUriForRead(const FilePath& content_uri) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_uri =
+ ConvertUTF8ToJavaString(env, content_uri.value());
+ jint fd = Java_ContentUriUtils_openContentUriForRead(
+ env, base::android::GetApplicationContext(), j_uri.obj());
+ if (fd < 0)
+ return base::kInvalidPlatformFileValue;
+ return fd;
+}
+
+} // namespace base
diff --git a/base/android/content_uri_utils.h b/base/android/content_uri_utils.h
new file mode 100644
index 0000000..e738803
--- /dev/null
+++ b/base/android/content_uri_utils.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_CONTENT_URI_UTILS_H_
+#define BASE_ANDROID_CONTENT_URI_UTILS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+
+namespace base {
+
+bool RegisterContentUriUtils(JNIEnv* env);
+
+// Opens a content uri for read and returns the file descriptor to the caller.
+// Returns -1 if the uri is invalid.
+BASE_EXPORT int OpenContentUriForRead(const FilePath& content_uri);
+
+// Check whether a content uri exists.
+BASE_EXPORT bool ContentUriExists(const FilePath& content_uri);
+
+} // namespace base
+
+#endif // BASE_ANDROID_CONTENT_URI_UTILS_H_
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java
new file mode 100644
index 0000000..c639396
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import org.chromium.base.CalledByNative;
+
+/**
+ * This class provides methods to access content URI schemes.
+ */
+abstract class ContentUriUtils {
+ private static final String TAG = "ContentUriUtils";
+
+ // Prevent instantiation.
+ private ContentUriUtils() {}
+
+ /**
+ * Opens the content URI for reading, and returns the file descriptor to
+ * the caller. The caller is responsible for closing the file desciptor.
+ *
+ * @param context {@link Context} in interest
+ * @param uriString the content URI to open
+ * @returns file desciptor upon sucess, or -1 otherwise.
+ */
+ @CalledByNative
+ public static int openContentUriForRead(Context context, String uriString) {
+ ParcelFileDescriptor pfd = getParcelFileDescriptor(context, uriString);
+ if (pfd != null) {
+ return pfd.detachFd();
+ }
+ return -1;
+ }
+
+ /**
+ * Check whether a content URI exists.
+ *
+ * @param context {@link Context} in interest.
+ * @param uriString the content URI to query.
+ * @returns true if the uri exists, or false otherwise.
+ */
+ @CalledByNative
+ public static boolean contentUriExists(Context context, String uriString) {
+ ParcelFileDescriptor pfd = getParcelFileDescriptor(context, uriString);
+ if (pfd == null) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Helper method to open a content URI and return the ParcelFileDescriptor.
+ *
+ * @param context {@link Context} in interest.
+ * @param uriString the content URI to open.
+ * @returns ParcelFileDescriptor of the content URI, or NULL if the file does not exist.
+ */
+ private static ParcelFileDescriptor getParcelFileDescriptor(Context context, String uriString) {
+ ContentResolver resolver = context.getContentResolver();
+ Uri uri = Uri.parse(uriString);
+
+ ParcelFileDescriptor pfd = null;
+ try {
+ pfd = resolver.openFileDescriptor(uri, "r");
+ } catch (java.io.FileNotFoundException e) {
+ Log.w(TAG, "Cannot find content uri: " + uriString, e);
+ }
+ return pfd;
+ }
+}
diff --git a/base/base.gyp b/base/base.gyp
index fb096b9..0192461 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -896,6 +896,14 @@
'test/test_file_util_linux.cc',
],
}],
+ ['OS == "android"', {
+ 'dependencies': [
+ 'base_unittests_jni_headers',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/base',
+ ],
+ }],
],
'sources': [
'test/expectations/expectation.cc',
@@ -948,6 +956,7 @@
'test/task_runner_test_template.h',
'test/test_file_util.cc',
'test/test_file_util.h',
+ 'test/test_file_util_android.cc',
'test/test_file_util_linux.cc',
'test/test_file_util_mac.cc',
'test/test_file_util_posix.cc',
@@ -1214,6 +1223,7 @@
'sources': [
'android/java/src/org/chromium/base/ActivityStatus.java',
'android/java/src/org/chromium/base/BuildInfo.java',
+ 'android/java/src/org/chromium/base/ContentUriUtils.java',
'android/java/src/org/chromium/base/CpuFeatures.java',
'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java',
'android/java/src/org/chromium/base/MemoryPressureListener.java',
@@ -1239,6 +1249,17 @@
'includes': [ '../build/jni_generator.gypi' ],
},
{
+ 'target_name': 'base_unittests_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'test/android/java/src/org/chromium/base/ContentUriTestUtils.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'base',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
'target_name': 'base_java',
'type': 'none',
'variables': {
@@ -1258,6 +1279,17 @@
],
},
{
+ 'target_name': 'base_java_unittest_support',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ ],
+ 'variables': {
+ 'java_in_dir': '../base/test/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
'target_name': 'base_java_activity_state',
'type': 'none',
# This target is used to auto-generate ActivityState.java
@@ -1339,6 +1371,7 @@
'type': 'none',
'dependencies': [
'base_java',
+ 'base_java_unittest_support',
'base_unittests',
],
'variables': {
diff --git a/base/base.gypi b/base/base.gypi
index 955233c..c46fac2 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -39,6 +39,8 @@
'android/base_jni_registrar.h',
'android/build_info.cc',
'android/build_info.h',
+ 'android/content_uri_utils.cc',
+ 'android/content_uri_utils.h',
'android/cpu_features.cc',
'android/fifo_utils.cc',
'android/fifo_utils.h',
diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc
index a2dd19b..4546e31 100644
--- a/base/file_util_posix.cc
+++ b/base/file_util_posix.cc
@@ -48,6 +48,7 @@
#include "base/time/time.h"
#if defined(OS_ANDROID)
+#include "base/android/content_uri_utils.h"
#include "base/os_compat_android.h"
#endif
@@ -79,6 +80,12 @@ static int CallLstat(const char *path, stat_wrapper_t *sb) {
ThreadRestrictions::AssertIOAllowed();
return lstat64(path, sb);
}
+#if defined(OS_ANDROID)
+static int CallFstat(int fd, stat_wrapper_t *sb) {
+ ThreadRestrictions::AssertIOAllowed();
+ return fstat64(fd, sb);
+}
+#endif
#endif
// Helper for NormalizeFilePath(), defined below.
@@ -308,6 +315,11 @@ bool CopyDirectory(const FilePath& from_path,
bool PathExists(const FilePath& path) {
ThreadRestrictions::AssertIOAllowed();
+#if defined(OS_ANDROID)
+ if (path.IsContentUri()) {
+ return ContentUriExists(path);
+ }
+#endif
return access(path.value().c_str(), F_OK) == 0;
}
@@ -569,8 +581,21 @@ bool IsLink(const FilePath& file_path) {
bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
stat_wrapper_t file_info;
- if (CallStat(file_path.value().c_str(), &file_info) != 0)
- return false;
+#if defined(OS_ANDROID)
+ if (file_path.IsContentUri()) {
+ int fd = OpenContentUriForRead(file_path);
+ if (fd < 0)
+ return false;
+ ScopedFD scoped_fd(&fd);
+ if (base::CallFstat(fd, &file_info) != 0)
+ return false;
+ } else {
+#endif // defined(OS_ANDROID)
+ if (CallStat(file_path.value().c_str(), &file_info) != 0)
+ return false;
+#if defined(OS_ANDROID)
+ }
+#endif // defined(OS_ANDROID)
results->is_directory = S_ISDIR(file_info.st_mode);
results->size = file_info.st_size;
#if defined(OS_MACOSX)
diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc
index 1ca70b4..3594749 100644
--- a/base/file_util_unittest.cc
+++ b/base/file_util_unittest.cc
@@ -33,6 +33,10 @@
#include "base/win/windows_version.h"
#endif
+#if defined(OS_ANDROID)
+#include "base/android/content_uri_utils.h"
+#endif
+
// This macro helps avoid wrapped lines in the test structs.
#define FPL(x) FILE_PATH_LITERAL(x)
@@ -2319,6 +2323,52 @@ TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) {
sub_dir_, text_file_, uid_, ok_gids_));
}
+#if defined(OS_ANDROID)
+TEST_F(FileUtilTest, ValidContentUriTest) {
+ // Get the test image path.
+ FilePath data_dir;
+ ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir));
+ data_dir = data_dir.AppendASCII("file_util");
+ ASSERT_TRUE(base::PathExists(data_dir));
+ FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png"));
+ int64 image_size;
+ file_util::GetFileSize(image_file, &image_size);
+ EXPECT_LT(0, image_size);
+
+ // Insert the image into MediaStore. MediaStore will do some conversions, and
+ // return the content URI.
+ base::FilePath path = file_util::InsertImageIntoMediaStore(image_file);
+ EXPECT_TRUE(path.IsContentUri());
+ EXPECT_TRUE(base::PathExists(path));
+ // The file size may not equal to the input image as MediaStore may convert
+ // the image.
+ int64 content_uri_size;
+ file_util::GetFileSize(path, &content_uri_size);
+ EXPECT_EQ(image_size, content_uri_size);
+
+ // We should be able to read the file.
+ char* buffer = new char[image_size];
+ int fd = base::OpenContentUriForRead(path);
+ EXPECT_LT(0, fd);
+ EXPECT_TRUE(file_util::ReadFromFD(fd, buffer, image_size));
+ delete[] buffer;
+}
+
+TEST_F(FileUtilTest, NonExistentContentUriTest) {
+ base::FilePath path("content://foo.bar");
+ EXPECT_TRUE(path.IsContentUri());
+ EXPECT_FALSE(base::PathExists(path));
+ // Size should be smaller than 0.
+ int64 size;
+ file_util::GetFileSize(path, &size);
+ EXPECT_GT(0, size);
+
+ // We should not be able to read the file.
+ int fd = base::OpenContentUriForRead(path);
+ EXPECT_EQ(-1, fd);
+}
+#endif
+
#endif // defined(OS_POSIX)
} // namespace
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index cfae3a5..4cfa2e6 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -1280,6 +1280,12 @@ FilePath FilePath::NormalizePathSeparators() const {
#endif
}
+#if defined(OS_ANDROID)
+bool FilePath::IsContentUri() const {
+ return StartsWithASCII(path_, "content://", false /*case_sensitive*/);
+}
+#endif
+
} // namespace base
void PrintTo(const base::FilePath& path, std::ostream* out) {
diff --git a/base/files/file_path.h b/base/files/file_path.h
index 4d03da4..33beb0b 100644
--- a/base/files/file_path.h
+++ b/base/files/file_path.h
@@ -387,6 +387,15 @@ class BASE_EXPORT FilePath {
const StringType& string2);
#endif
+#if defined(OS_ANDROID)
+ // On android, file selection dialog can return a file with content uri
+ // scheme(starting with content://). Content uri needs to be opened with
+ // ContentResolver to guarantee that the app has appropriate permissions
+ // to access it.
+ // Returns true if the path is a content uri, or false otherwise.
+ bool IsContentUri() const;
+#endif
+
private:
// Remove trailing separators from this object. If the path is absolute, it
// will never be stripped any more than to refer to the absolute root
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index 8b2fcf5..1b6e465 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -1228,4 +1228,33 @@ TEST_F(FilePathTest, AsEndingWithSeparator) {
}
}
+#if defined(OS_ANDROID)
+TEST_F(FilePathTest, ContentUriTest) {
+ const struct UnaryBooleanTestData cases[] = {
+ { FPL("content://foo.bar"), true },
+ { FPL("content://foo.bar/"), true },
+ { FPL("content://foo/bar"), true },
+ { FPL("CoNTenT://foo.bar"), true },
+ { FPL("content://"), true },
+ { FPL("content:///foo.bar"), true },
+ { FPL("content://3foo/bar"), true },
+ { FPL("content://_foo/bar"), true },
+ { FPL(".. "), false },
+ { FPL("foo.bar"), false },
+ { FPL("content:foo.bar"), false },
+ { FPL("content:/foo.ba"), false },
+ { FPL("content:/dir/foo.bar"), false },
+ { FPL("content: //foo.bar"), false },
+ { FPL("content%2a%2f%2f"), false },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ bool observed = input.IsContentUri();
+ EXPECT_EQ(cases[i].expected, observed) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+#endif
+
} // namespace base
diff --git a/base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java b/base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java
new file mode 100644
index 0000000..f8241cd
--- /dev/null
+++ b/base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import org.chromium.base.CalledByNative;
+
+/**
+ * Utilities for testing operations on content URI.
+ */
+public class ContentUriTestUtils {
+ /**
+ * Insert an image into the MediaStore, and return the content URI. If the
+ * image already exists in the MediaStore, just retrieve the URI.
+ *
+ * @param context Application context.
+ * @param path Path to the image file.
+ * @return Content URI of the image.
+ */
+ @CalledByNative
+ private static String insertImageIntoMediaStore(Context context, String path) {
+ // Check whether the content URI exists.
+ Cursor c = context.getContentResolver().query(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] { MediaStore.Video.VideoColumns._ID },
+ MediaStore.Images.Media.DATA + " LIKE ?",
+ new String[] { path },
+ null);
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ int id = c.getInt(0);
+ return Uri.withAppendedPath(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + id).toString();
+ }
+
+ // Insert the content URI into MediaStore.
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.DATA, path);
+ Uri uri = context.getContentResolver().insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+ return uri.toString();
+ }
+}
diff --git a/base/test/data/file_util/red.png b/base/test/data/file_util/red.png
new file mode 100644
index 0000000..0806141
--- /dev/null
+++ b/base/test/data/file_util/red.png
Binary files differ
diff --git a/base/test/run_all_unittests.cc b/base/test/run_all_unittests.cc
index e561f0e..3b5ebfe 100644
--- a/base/test/run_all_unittests.cc
+++ b/base/test/run_all_unittests.cc
@@ -7,6 +7,11 @@
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/test/test_file_util.h"
+#endif
+
namespace {
class NoAtExitBaseTestSuite : public base::TestSuite {
@@ -23,7 +28,10 @@ int RunTestSuite(int argc, char** argv) {
} // namespace
int main(int argc, char** argv) {
-#if !defined(OS_ANDROID)
+#if defined(OS_ANDROID)
+ JNIEnv* env = base::android::AttachCurrentThread();
+ file_util::RegisterContentUriTestUtils(env);
+#else
base::AtExitManager at_exit;
#endif
return base::LaunchUnitTests(argc,
diff --git a/base/test/test_file_util.h b/base/test/test_file_util.h
index cf20221..656babd 100644
--- a/base/test/test_file_util.h
+++ b/base/test/test_file_util.h
@@ -12,6 +12,11 @@
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
+#if defined(OS_ANDROID)
+#include <jni.h>
+#include "base/basictypes.h"
+#endif
+
namespace base {
class FilePath;
@@ -58,6 +63,15 @@ base::FilePath WStringAsFilePath(const std::wstring& path);
bool MakeFileUnreadable(const base::FilePath& path) WARN_UNUSED_RESULT;
bool MakeFileUnwritable(const base::FilePath& path) WARN_UNUSED_RESULT;
+#if defined(OS_ANDROID)
+// Register the ContentUriTestUrils JNI bindings.
+bool RegisterContentUriTestUtils(JNIEnv* env);
+
+// Insert an image file into the MediaStore, and retrieve the content URI for
+// testing purpose.
+base::FilePath InsertImageIntoMediaStore(const base::FilePath& path);
+#endif // defined(OS_ANDROID)
+
// Saves the current permissions for a path, and restores it on destruction.
class PermissionRestorer {
public:
diff --git a/base/test/test_file_util_android.cc b/base/test/test_file_util_android.cc
new file mode 100644
index 0000000..9bee1d1
--- /dev/null
+++ b/base/test/test_file_util_android.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "jni/ContentUriTestUtils_jni.h"
+
+namespace file_util {
+
+bool RegisterContentUriTestUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+base::FilePath InsertImageIntoMediaStore(const base::FilePath& path) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_path =
+ base::android::ConvertUTF8ToJavaString(env, path.value());
+ ScopedJavaLocalRef<jstring> j_uri =
+ Java_ContentUriTestUtils_insertImageIntoMediaStore(
+ env, base::android::GetApplicationContext(), j_path.obj());
+ std::string uri = base::android::ConvertJavaStringToUTF8(j_uri);
+ return base::FilePath(uri);
+}
+
+} // namespace file_util
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index b0ac66d..85ec83a 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -135,6 +135,22 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
return (it->second & permissions) == permissions;
}
+#if defined(OS_ANDROID)
+ // Determine if the certain permissions have been granted to a content URI.
+ bool HasPermissionsForContentUri(const base::FilePath& file,
+ int permissions) {
+ DCHECK(!file.empty());
+ DCHECK(file.IsContentUri());
+ if (!permissions)
+ return false;
+ base::FilePath file_path = file.StripTrailingSeparators();
+ FileMap::const_iterator it = file_permissions_.find(file_path);
+ if (it != file_permissions_.end())
+ return (it->second & permissions) == permissions;
+ return false;
+ }
+#endif
+
void GrantBindings(int bindings) {
enabled_bindings_ |= bindings;
}
@@ -171,6 +187,10 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
// Determine if the certain permissions have been granted to a file.
bool HasPermissionsForFile(const base::FilePath& file, int permissions) {
+#if defined(OS_ANDROID)
+ if (file.IsContentUri())
+ return HasPermissionsForContentUri(file, permissions);
+#endif
if (!permissions || file.empty() || !file.IsAbsolute())
return false;
base::FilePath current_path = file.StripTrailingSeparators();
diff --git a/net/base/file_stream_context.cc b/net/base/file_stream_context.cc
index 2e77475..e7fe100 100644
--- a/net/base/file_stream_context.cc
+++ b/net/base/file_stream_context.cc
@@ -11,6 +11,10 @@
#include "net/base/file_stream_net_log_parameters.h"
#include "net/base/net_errors.h"
+#if defined(OS_ANDROID)
+#include "base/android/content_uri_utils.h"
+#endif
+
namespace {
void CallInt64ToInt(const net::CompletionCallback& callback, int64 result) {
@@ -193,13 +197,24 @@ void FileStream::Context::BeginOpenEvent(const base::FilePath& path) {
FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
const base::FilePath& path, int open_flags) {
- // FileStream::Context actually closes the file asynchronously, independently
- // from FileStream's destructor. It can cause problems for users wanting to
- // delete the file right after FileStream deletion. Thus we are always
- // adding SHARE_DELETE flag to accommodate such use case.
- open_flags |= base::PLATFORM_FILE_SHARE_DELETE;
- base::PlatformFile file =
- base::CreatePlatformFile(path, open_flags, NULL, NULL);
+ base::PlatformFile file;
+#if defined(OS_ANDROID)
+ if (path.IsContentUri()) {
+ // Check that only Read flags are set.
+ DCHECK_EQ(open_flags & ~base::PLATFORM_FILE_ASYNC,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
+ file = base::OpenContentUriForRead(path);
+ } else {
+#endif // defined(OS_ANDROID)
+ // FileStream::Context actually closes the file asynchronously,
+ // independently from FileStream's destructor. It can cause problems for
+ // users wanting to delete the file right after FileStream deletion. Thus
+ // we are always adding SHARE_DELETE flag to accommodate such use case.
+ open_flags |= base::PLATFORM_FILE_SHARE_DELETE;
+ file = base::CreatePlatformFile(path, open_flags, NULL, NULL);
+#if defined(OS_ANDROID)
+ }
+#endif // defined(OS_ANDROID)
if (file == base::kInvalidPlatformFileValue)
return OpenResult(file, IOResult::FromOSError(GetLastErrno()));
diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc
index c76f3d9..e721569 100644
--- a/net/base/file_stream_unittest.cc
+++ b/net/base/file_stream_unittest.cc
@@ -21,6 +21,10 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+#if defined(OS_ANDROID)
+#include "base/test/test_file_util.h"
+#endif
+
namespace net {
namespace {
@@ -1173,6 +1177,55 @@ TEST_F(FileStreamTest, AsyncReadError) {
base::ClosePlatformFile(file);
}
+#if defined(OS_ANDROID)
+TEST_F(FileStreamTest, ContentUriAsyncRead) {
+ base::FilePath test_dir;
+ PathService::Get(base::DIR_SOURCE_ROOT, &test_dir);
+ test_dir = test_dir.AppendASCII("net");
+ test_dir = test_dir.AppendASCII("data");
+ test_dir = test_dir.AppendASCII("file_stream_unittest");
+ ASSERT_TRUE(base::PathExists(test_dir));
+ base::FilePath image_file = test_dir.Append(FILE_PATH_LITERAL("red.png"));
+
+ // Insert the image into MediaStore. MediaStore will do some conversions, and
+ // return the content URI.
+ base::FilePath path = file_util::InsertImageIntoMediaStore(image_file);
+ EXPECT_TRUE(path.IsContentUri());
+ EXPECT_TRUE(base::PathExists(path));
+ int64 file_size;
+ EXPECT_TRUE(file_util::GetFileSize(path, &file_size));
+ EXPECT_LT(0, file_size);
+
+ FileStream stream(NULL, base::MessageLoopProxy::current());
+ int flags = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_ASYNC;
+ TestCompletionCallback callback;
+ int rv = stream.Open(path, flags, callback.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ int64 total_bytes_avail = stream.Available();
+ EXPECT_EQ(file_size, total_bytes_avail);
+
+ int total_bytes_read = 0;
+
+ std::string data_read;
+ for (;;) {
+ scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(4);
+ rv = stream.Read(buf.get(), buf->size(), callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_LE(0, rv);
+ if (rv <= 0)
+ break;
+ total_bytes_read += rv;
+ data_read.append(buf->data(), rv);
+ }
+ EXPECT_EQ(file_size, total_bytes_read);
+}
+#endif
+
} // namespace
} // namespace net
diff --git a/net/data/file_stream_unittest/red.png b/net/data/file_stream_unittest/red.png
new file mode 100644
index 0000000..0806141
--- /dev/null
+++ b/net/data/file_stream_unittest/red.png
Binary files differ
diff --git a/net/net.gyp b/net/net.gyp
index 3e5b04b..4f16adb 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -2993,6 +2993,7 @@
'target_name': 'net_unittests_apk',
'type': 'none',
'dependencies': [
+ '../base/base.gyp:base_java_unittest_support',
'net_java',
'net_javatests',
'net_unittests',
diff --git a/net/test/run_all_unittests.cc b/net/test/run_all_unittests.cc
index 3a51ba0..9b5dad2 100644
--- a/net/test/run_all_unittests.cc
+++ b/net/test/run_all_unittests.cc
@@ -13,6 +13,7 @@
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
+#include "base/test/test_file_util.h"
#include "net/android/net_jni_registrar.h"
#endif
@@ -31,6 +32,7 @@ int main(int argc, char** argv) {
// Register JNI bindings for android. Doing it early as the test suite setup
// may initiate a call to Java.
net::android::RegisterJni(base::android::AttachCurrentThread());
+ file_util::RegisterContentUriTestUtils(base::android::AttachCurrentThread());
#endif
NetTestSuite test_suite(argc, argv);
diff --git a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
index f495767..4e269b0 100644
--- a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
+++ b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
@@ -165,27 +165,11 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{
return;
}
- Cursor cursor = null;
- try {
- // We get back a content:// URI from the system if the user picked a file from the
- // gallery. The ContentView has functionality that will convert that content:// URI to
- // a file path on disk that Chromium understands.
- cursor = contentResolver.query(results.getData(),
- new String[] { MediaStore.MediaColumns.DATA }, null, null, null);
- if (cursor != null) {
- if (cursor.getCount() == 1) {
- cursor.moveToFirst();
- String path = cursor.getString(0);
- if (path != null) {
- // Not all providers support the MediaStore.DATA column. For example,
- // Gallery3D (com.android.gallery3d.provider) does not support it for
- // Picasa Web Album images.
- nativeOnFileSelected(mNativeSelectFileDialog, path);
- return;
- }
- }
- }
- } finally { if (cursor != null) { cursor.close(); } }
+ if (results.getScheme() != null
+ && results.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
+ nativeOnFileSelected(mNativeSelectFileDialog, results.getData().toString());
+ return;
+ }
onFileNotSelected();
window.showError(R.string.opening_file_error);