// Copyright 2013 The Chromium Authors // 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 #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/files/file.h" #include "base/logging.h" #include "base/time/time.h" // Must come after all headers that specialize FromJniType() / ToJniType(). #include "base/content_uri_utils_jni/ContentUriUtils_jni.h" using base::android::ConvertUTF8ToJavaString; using base::android::JavaParamRef; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; namespace base { namespace { std::string SafeConvertJavaStringToUTF8(JNIEnv* env, const JavaRef& str) { if (str.is_null()) { return std::string(); } return base::android::ConvertJavaStringToUTF8(env, str); } } // namespace namespace internal { bool ContentUriExists(const FilePath& content_uri) { JNIEnv* env = android::AttachCurrentThread(); return Java_ContentUriUtils_contentUriExists(env, content_uri.value()); } std::optional TranslateOpenFlagsToJavaMode(uint32_t open_flags) { // The allowable modes from ParcelFileDescriptor#parseMode() are // ("r", "w", "wt", "wa", "rw", "rwt"), we disallow "w" which has been the // source of android security issues. // Ignore async. open_flags &= ~File::FLAG_ASYNC; switch (open_flags) { case File::FLAG_OPEN | File::FLAG_READ: return "r"; case File::FLAG_OPEN_ALWAYS | File::FLAG_READ | File::FLAG_WRITE: return "rw"; case File::FLAG_OPEN_ALWAYS | File::FLAG_APPEND: return "wa"; case File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE: return "rwt"; case File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE: return "wt"; default: return std::nullopt; } } int OpenContentUri(const FilePath& content_uri, uint32_t open_flags) { JNIEnv* env = base::android::AttachCurrentThread(); auto mode = TranslateOpenFlagsToJavaMode(open_flags); CHECK(mode.has_value()) << "Unsupported flags=0x" << std::hex << open_flags; return Java_ContentUriUtils_openContentUri(env, content_uri.value(), *mode); } bool ContentUriGetFileInfo(const FilePath& content_uri, FileEnumerator::FileInfo* info) { JNIEnv* env = android::AttachCurrentThread(); std::vector list; Java_ContentUriUtils_getFileInfo(env, content_uri.value(), reinterpret_cast(&list)); // Java will call back sync to AddFileInfoToVector(&list). if (list.empty()) { return false; } // Android can return -1 for unknown size, which // we can't deal with, so we will consider that the file wasn't found. if (list[0].GetSize() < 0) { LOG(ERROR) << "Unknown file length for " << content_uri; return false; } *info = std::move(list[0]); return true; } std::vector ListContentUriDirectory( const FilePath& content_uri) { JNIEnv* env = android::AttachCurrentThread(); std::vector result; Java_ContentUriUtils_listDirectory(env, content_uri.value(), reinterpret_cast(&result)); // Java will call back sync to AddFileInfoToVector(&result). return result; } bool DeleteContentUri(const FilePath& content_uri) { DCHECK(content_uri.IsContentUri()); JNIEnv* env = android::AttachCurrentThread(); return Java_ContentUriUtils_delete(env, content_uri.value()); } bool IsDocumentUri(const FilePath& content_uri) { DCHECK(content_uri.IsContentUri()); JNIEnv* env = android::AttachCurrentThread(); return Java_ContentUriUtils_isDocumentUri(env, content_uri.value()); } } // namespace internal void JNI_ContentUriUtils_AddFileInfoToVector( JNIEnv* env, jlong vector_pointer, const JavaParamRef& uri, const JavaParamRef& display_name, jboolean is_directory, jlong size, jlong last_modified) { auto* result = reinterpret_cast*>(vector_pointer); result->emplace_back(FilePath(SafeConvertJavaStringToUTF8(env, uri)), FilePath(SafeConvertJavaStringToUTF8(env, display_name)), is_directory, size, Time::FromMillisecondsSinceUnixEpoch(last_modified)); } std::string GetContentUriMimeType(const FilePath& content_uri) { JNIEnv* env = android::AttachCurrentThread(); ScopedJavaLocalRef j_mime = Java_ContentUriUtils_getMimeType(env, content_uri.value()); return SafeConvertJavaStringToUTF8(env, j_mime); } bool MaybeGetFileDisplayName(const FilePath& content_uri, std::u16string* file_display_name) { if (!content_uri.IsContentUri()) return false; DCHECK(file_display_name); JNIEnv* env = android::AttachCurrentThread(); ScopedJavaLocalRef j_display_name = Java_ContentUriUtils_maybeGetDisplayName(env, content_uri.value()); if (j_display_name.is_null()) return false; *file_display_name = android::ConvertJavaStringToUTF16(j_display_name); return true; } FilePath ContentUriBuildDocumentUriUsingTree( const FilePath& tree_uri, const std::string& encoded_document_id) { JNIEnv* env = android::AttachCurrentThread(); ScopedJavaLocalRef j_uri = Java_ContentUriUtils_buildDocumentUriUsingTree(env, tree_uri.value(), encoded_document_id); return FilePath(SafeConvertJavaStringToUTF8(env, j_uri)); } FilePath ContentUriGetChildDocumentOrQuery(const FilePath& parent, const std::string& display_name, const std::string& mime_type, bool is_directory, bool create) { JNIEnv* env = android::AttachCurrentThread(); ScopedJavaLocalRef j_uri = Java_ContentUriUtils_getChildDocumentOrQuery( env, parent.value(), display_name, mime_type, is_directory, create); return FilePath(SafeConvertJavaStringToUTF8(env, j_uri)); } bool ContentUriIsCreateChildDocumentQuery(const FilePath& content_uri) { JNIEnv* env = android::AttachCurrentThread(); return Java_ContentUriUtils_isCreateChildDocumentQuery(env, content_uri.value()); } FilePath ContentUriGetDocumentFromQuery(const FilePath& content_uri, bool create) { JNIEnv* env = android::AttachCurrentThread(); ScopedJavaLocalRef j_uri = Java_ContentUriUtils_getDocumentFromQuery( env, content_uri.value(), create); return FilePath(SafeConvertJavaStringToUTF8(env, j_uri)); } } // namespace base