• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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