1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/android/content_uri_utils.h"
6
7 #include <sys/stat.h>
8
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/files/file.h"
13 #include "base/logging.h"
14 #include "base/time/time.h"
15
16 // Must come after all headers that specialize FromJniType() / ToJniType().
17 #include "base/content_uri_utils_jni/ContentUriUtils_jni.h"
18
19 using base::android::ConvertUTF8ToJavaString;
20 using base::android::JavaParamRef;
21 using base::android::JavaRef;
22 using base::android::ScopedJavaLocalRef;
23
24 namespace base {
25 namespace {
SafeConvertJavaStringToUTF8(JNIEnv * env,const JavaRef<jstring> & str)26 std::string SafeConvertJavaStringToUTF8(JNIEnv* env,
27 const JavaRef<jstring>& str) {
28 if (str.is_null()) {
29 return std::string();
30 }
31 return base::android::ConvertJavaStringToUTF8(env, str);
32 }
33 } // namespace
34
35 namespace internal {
36
ContentUriExists(const FilePath & content_uri)37 bool ContentUriExists(const FilePath& content_uri) {
38 JNIEnv* env = android::AttachCurrentThread();
39 return Java_ContentUriUtils_contentUriExists(env, content_uri.value());
40 }
41
TranslateOpenFlagsToJavaMode(uint32_t open_flags)42 std::optional<std::string> TranslateOpenFlagsToJavaMode(uint32_t open_flags) {
43 // The allowable modes from ParcelFileDescriptor#parseMode() are
44 // ("r", "w", "wt", "wa", "rw", "rwt"), we disallow "w" which has been the
45 // source of android security issues.
46
47 // Ignore async.
48 open_flags &= ~File::FLAG_ASYNC;
49
50 switch (open_flags) {
51 case File::FLAG_OPEN | File::FLAG_READ:
52 return "r";
53 case File::FLAG_OPEN_ALWAYS | File::FLAG_READ | File::FLAG_WRITE:
54 return "rw";
55 case File::FLAG_OPEN_ALWAYS | File::FLAG_APPEND:
56 return "wa";
57 case File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE:
58 return "rwt";
59 case File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE:
60 return "wt";
61 default:
62 return std::nullopt;
63 }
64 }
65
OpenContentUri(const FilePath & content_uri,uint32_t open_flags)66 int OpenContentUri(const FilePath& content_uri, uint32_t open_flags) {
67 JNIEnv* env = base::android::AttachCurrentThread();
68 auto mode = TranslateOpenFlagsToJavaMode(open_flags);
69 CHECK(mode.has_value()) << "Unsupported flags=0x" << std::hex << open_flags;
70 return Java_ContentUriUtils_openContentUri(env, content_uri.value(), *mode);
71 }
72
ContentUriGetFileInfo(const FilePath & content_uri,FileEnumerator::FileInfo * info)73 bool ContentUriGetFileInfo(const FilePath& content_uri,
74 FileEnumerator::FileInfo* info) {
75 JNIEnv* env = android::AttachCurrentThread();
76 std::vector<FileEnumerator::FileInfo> list;
77 Java_ContentUriUtils_getFileInfo(env, content_uri.value(),
78 reinterpret_cast<jlong>(&list));
79 // Java will call back sync to AddFileInfoToVector(&list).
80 if (list.empty()) {
81 return false;
82 }
83 // Android can return -1 for unknown size, which
84 // we can't deal with, so we will consider that the file wasn't found.
85 if (list[0].GetSize() < 0) {
86 LOG(ERROR) << "Unknown file length for " << content_uri;
87 return false;
88 }
89 *info = std::move(list[0]);
90 return true;
91 }
92
ListContentUriDirectory(const FilePath & content_uri)93 std::vector<FileEnumerator::FileInfo> ListContentUriDirectory(
94 const FilePath& content_uri) {
95 JNIEnv* env = android::AttachCurrentThread();
96 std::vector<FileEnumerator::FileInfo> result;
97 Java_ContentUriUtils_listDirectory(env, content_uri.value(),
98 reinterpret_cast<jlong>(&result));
99 // Java will call back sync to AddFileInfoToVector(&result).
100 return result;
101 }
102
DeleteContentUri(const FilePath & content_uri)103 bool DeleteContentUri(const FilePath& content_uri) {
104 DCHECK(content_uri.IsContentUri());
105 JNIEnv* env = android::AttachCurrentThread();
106 return Java_ContentUriUtils_delete(env, content_uri.value());
107 }
108
IsDocumentUri(const FilePath & content_uri)109 bool IsDocumentUri(const FilePath& content_uri) {
110 DCHECK(content_uri.IsContentUri());
111 JNIEnv* env = android::AttachCurrentThread();
112 return Java_ContentUriUtils_isDocumentUri(env, content_uri.value());
113 }
114
115 } // namespace internal
116
JNI_ContentUriUtils_AddFileInfoToVector(JNIEnv * env,jlong vector_pointer,const JavaParamRef<jstring> & uri,const JavaParamRef<jstring> & display_name,jboolean is_directory,jlong size,jlong last_modified)117 void JNI_ContentUriUtils_AddFileInfoToVector(
118 JNIEnv* env,
119 jlong vector_pointer,
120 const JavaParamRef<jstring>& uri,
121 const JavaParamRef<jstring>& display_name,
122 jboolean is_directory,
123 jlong size,
124 jlong last_modified) {
125 auto* result =
126 reinterpret_cast<std::vector<FileEnumerator::FileInfo>*>(vector_pointer);
127 result->emplace_back(FilePath(SafeConvertJavaStringToUTF8(env, uri)),
128 FilePath(SafeConvertJavaStringToUTF8(env, display_name)),
129 is_directory, size,
130 Time::FromMillisecondsSinceUnixEpoch(last_modified));
131 }
132
GetContentUriMimeType(const FilePath & content_uri)133 std::string GetContentUriMimeType(const FilePath& content_uri) {
134 JNIEnv* env = android::AttachCurrentThread();
135 ScopedJavaLocalRef<jstring> j_mime =
136 Java_ContentUriUtils_getMimeType(env, content_uri.value());
137 return SafeConvertJavaStringToUTF8(env, j_mime);
138 }
139
MaybeGetFileDisplayName(const FilePath & content_uri,std::u16string * file_display_name)140 bool MaybeGetFileDisplayName(const FilePath& content_uri,
141 std::u16string* file_display_name) {
142 if (!content_uri.IsContentUri())
143 return false;
144
145 DCHECK(file_display_name);
146
147 JNIEnv* env = android::AttachCurrentThread();
148 ScopedJavaLocalRef<jstring> j_display_name =
149 Java_ContentUriUtils_maybeGetDisplayName(env, content_uri.value());
150
151 if (j_display_name.is_null())
152 return false;
153
154 *file_display_name = android::ConvertJavaStringToUTF16(j_display_name);
155 return true;
156 }
157
ContentUriBuildDocumentUriUsingTree(const FilePath & tree_uri,const std::string & encoded_document_id)158 FilePath ContentUriBuildDocumentUriUsingTree(
159 const FilePath& tree_uri,
160 const std::string& encoded_document_id) {
161 JNIEnv* env = android::AttachCurrentThread();
162 ScopedJavaLocalRef<jstring> j_uri =
163 Java_ContentUriUtils_buildDocumentUriUsingTree(env, tree_uri.value(),
164 encoded_document_id);
165 return FilePath(SafeConvertJavaStringToUTF8(env, j_uri));
166 }
167
ContentUriGetChildDocumentOrQuery(const FilePath & parent,const std::string & display_name,const std::string & mime_type,bool is_directory,bool create)168 FilePath ContentUriGetChildDocumentOrQuery(const FilePath& parent,
169 const std::string& display_name,
170 const std::string& mime_type,
171 bool is_directory,
172 bool create) {
173 JNIEnv* env = android::AttachCurrentThread();
174 ScopedJavaLocalRef<jstring> j_uri =
175 Java_ContentUriUtils_getChildDocumentOrQuery(
176 env, parent.value(), display_name, mime_type, is_directory, create);
177 return FilePath(SafeConvertJavaStringToUTF8(env, j_uri));
178 }
179
ContentUriIsCreateChildDocumentQuery(const FilePath & content_uri)180 bool ContentUriIsCreateChildDocumentQuery(const FilePath& content_uri) {
181 JNIEnv* env = android::AttachCurrentThread();
182 return Java_ContentUriUtils_isCreateChildDocumentQuery(env,
183 content_uri.value());
184 }
185
ContentUriGetDocumentFromQuery(const FilePath & content_uri,bool create)186 FilePath ContentUriGetDocumentFromQuery(const FilePath& content_uri,
187 bool create) {
188 JNIEnv* env = android::AttachCurrentThread();
189 ScopedJavaLocalRef<jstring> j_uri = Java_ContentUriUtils_getDocumentFromQuery(
190 env, content_uri.value(), create);
191 return FilePath(SafeConvertJavaStringToUTF8(env, j_uri));
192 }
193
194 } // namespace base
195