• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specic language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "MediaProviderWrapper.h"
18 #include "libfuse_jni/ReaddirHelper.h"
19 
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <jni.h>
23 #include <nativehelper/scoped_local_ref.h>
24 #include <nativehelper/scoped_primitive_array.h>
25 #include <nativehelper/scoped_utf_chars.h>
26 
27 #include <pthread.h>
28 
29 #include <mutex>
30 #include <unordered_map>
31 
32 namespace mediaprovider {
33 namespace fuse {
34 using std::string;
35 
36 namespace {
37 
38 constexpr const char* kPropRedactionEnabled = "persist.sys.fuse.redaction-enabled";
39 
40 constexpr uid_t ROOT_UID = 0;
41 constexpr uid_t SHELL_UID = 2000;
42 
43 // These need to stay in sync with MediaProvider.java's DIRECTORY_ACCESS_FOR_* constants.
44 enum DirectoryAccessRequestType {
45     kReadDirectoryRequest = 1,
46     kWriteDirectoryRequest = 2,
47     kCreateDirectoryRequest = 3,
48     kDeleteDirectoryRequest = 4,
49 };
50 
51 /** Private helper functions **/
52 
shouldBypassMediaProvider(uid_t uid)53 inline bool shouldBypassMediaProvider(uid_t uid) {
54     return uid == SHELL_UID || uid == ROOT_UID;
55 }
56 
CheckForJniException(JNIEnv * env)57 static bool CheckForJniException(JNIEnv* env) {
58     if (env->ExceptionCheck()) {
59         env->ExceptionDescribe();
60         env->ExceptionClear();
61         return true;
62     }
63     return false;
64 }
65 
66 /**
67  * Auxiliary for caching class fields
68  */
CacheField(JNIEnv * env,jclass clazz,const char field_name[],const char type[])69 static jfieldID CacheField(JNIEnv* env, jclass clazz, const char field_name[], const char type[]) {
70     jfieldID fid;
71     string actual_field_name(field_name);
72     fid = env->GetFieldID(clazz, actual_field_name.c_str(), type);
73     if (!fid) {
74         LOG(FATAL) << "Error caching field: " << field_name << type;
75     }
76     return fid;
77 }
78 
insertFileInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_insert_file,const string & path,uid_t uid)79 int insertFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_insert_file,
80                        const string& path, uid_t uid) {
81     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
82     int res = env->CallIntMethod(media_provider_object, mid_insert_file, j_path.get(), uid);
83 
84     if (CheckForJniException(env)) {
85         return EFAULT;
86     }
87     return res;
88 }
89 
deleteFileInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_delete_file,const string & path,uid_t uid)90 int deleteFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_delete_file,
91                        const string& path, uid_t uid) {
92     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
93     int res = env->CallIntMethod(media_provider_object, mid_delete_file, j_path.get(), uid);
94 
95     if (CheckForJniException(env)) {
96         return EFAULT;
97     }
98     return res;
99 }
100 
isDirAccessAllowedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_is_diraccess_allowed,const string & path,uid_t uid,int accessType)101 int isDirAccessAllowedInternal(JNIEnv* env, jobject media_provider_object,
102                                jmethodID mid_is_diraccess_allowed, const string& path, uid_t uid,
103                                int accessType) {
104     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
105     int res = env->CallIntMethod(media_provider_object, mid_is_diraccess_allowed, j_path.get(), uid,
106                                  accessType);
107 
108     if (CheckForJniException(env)) {
109         return EFAULT;
110     }
111     return res;
112 }
113 
isUidAllowedAccessToDataOrObbPathInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_is_uid_allowed_path_access_,uid_t uid,const string & path)114 bool isUidAllowedAccessToDataOrObbPathInternal(JNIEnv* env, jobject media_provider_object,
115                                                jmethodID mid_is_uid_allowed_path_access_, uid_t uid,
116                                                const string& path) {
117     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
118     bool res = env->CallBooleanMethod(media_provider_object, mid_is_uid_allowed_path_access_, uid,
119                                       j_path.get());
120 
121     if (CheckForJniException(env)) {
122         return false;
123     }
124     return res;
125 }
126 
getFilesInDirectoryInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_get_files_in_dir,uid_t uid,const string & path)127 std::vector<std::shared_ptr<DirectoryEntry>> getFilesInDirectoryInternal(
128         JNIEnv* env, jobject media_provider_object, jmethodID mid_get_files_in_dir, uid_t uid,
129         const string& path) {
130     std::vector<std::shared_ptr<DirectoryEntry>> directory_entries;
131     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
132 
133     ScopedLocalRef<jobjectArray> files_list(
134             env, static_cast<jobjectArray>(env->CallObjectMethod(
135                          media_provider_object, mid_get_files_in_dir, j_path.get(), uid)));
136 
137     if (CheckForJniException(env)) {
138         directory_entries.push_back(std::make_shared<DirectoryEntry>("", EFAULT));
139         return directory_entries;
140     }
141 
142     int de_count = env->GetArrayLength(files_list.get());
143     if (de_count == 1) {
144         ScopedLocalRef<jstring> j_d_name(env,
145                                          (jstring)env->GetObjectArrayElement(files_list.get(), 0));
146         ScopedUtfChars d_name(env, j_d_name.get());
147         if (d_name.c_str() == nullptr) {
148             LOG(ERROR) << "Error reading file name returned from MediaProvider at index " << 0;
149             directory_entries.push_back(std::make_shared<DirectoryEntry>("", EFAULT));
150             return directory_entries;
151         } else if (d_name.c_str()[0] == '\0') {
152             // Calling package has no storage permissions.
153             directory_entries.push_back(std::make_shared<DirectoryEntry>("", EPERM));
154             return directory_entries;
155         }
156     }
157 
158     for (int i = 0; i < de_count; i++) {
159         ScopedLocalRef<jstring> j_d_name(env,
160                                          (jstring)env->GetObjectArrayElement(files_list.get(), i));
161         ScopedUtfChars d_name(env, j_d_name.get());
162 
163         if (d_name.c_str() == nullptr) {
164             LOG(ERROR) << "Error reading file name returned from MediaProvider at index " << i;
165             directory_entries.resize(0);
166             directory_entries.push_back(std::make_shared<DirectoryEntry>("", EFAULT));
167             break;
168         }
169         directory_entries.push_back(std::make_shared<DirectoryEntry>(d_name.c_str(), DT_REG));
170     }
171     return directory_entries;
172 }
173 
renameInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_rename,const string & old_path,const string & new_path,uid_t uid)174 int renameInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_rename,
175                    const string& old_path, const string& new_path, uid_t uid) {
176     ScopedLocalRef<jstring> j_old_path(env, env->NewStringUTF(old_path.c_str()));
177     ScopedLocalRef<jstring> j_new_path(env, env->NewStringUTF(new_path.c_str()));
178     int res = env->CallIntMethod(media_provider_object, mid_rename, j_old_path.get(),
179                                  j_new_path.get(), uid);
180 
181     if (CheckForJniException(env)) {
182         return EFAULT;
183     }
184     return res;
185 }
186 
onFileCreatedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_on_file_created,const string & path)187 void onFileCreatedInternal(JNIEnv* env, jobject media_provider_object,
188                            jmethodID mid_on_file_created, const string& path) {
189     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
190 
191     env->CallVoidMethod(media_provider_object, mid_on_file_created, j_path.get());
192     CheckForJniException(env);
193     return;
194 }
195 
196 }  // namespace
197 /*****************************************************************************************/
198 /******************************* Public API Implementation *******************************/
199 /*****************************************************************************************/
200 
201 JavaVM* MediaProviderWrapper::gJavaVm = nullptr;
202 pthread_key_t MediaProviderWrapper::gJniEnvKey;
203 
OneTimeInit(JavaVM * vm)204 void MediaProviderWrapper::OneTimeInit(JavaVM* vm) {
205     gJavaVm = vm;
206     CHECK(gJavaVm != nullptr);
207 
208     pthread_key_create(&MediaProviderWrapper::gJniEnvKey,
209                        MediaProviderWrapper::DetachThreadFunction);
210 }
211 
MediaProviderWrapper(JNIEnv * env,jobject media_provider)212 MediaProviderWrapper::MediaProviderWrapper(JNIEnv* env, jobject media_provider) {
213     if (!media_provider) {
214         LOG(FATAL) << "MediaProvider is null!";
215     }
216 
217     media_provider_object_ = reinterpret_cast<jobject>(env->NewGlobalRef(media_provider));
218     media_provider_class_ = env->FindClass("com/android/providers/media/MediaProvider");
219     if (!media_provider_class_) {
220         LOG(FATAL) << "Could not find class MediaProvider";
221     }
222     media_provider_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(media_provider_class_));
223 
224     // Cache methods - Before calling a method, make sure you cache it here
225     mid_insert_file_ = CacheMethod(env, "insertFileIfNecessary", "(Ljava/lang/String;I)I");
226     mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I");
227     mid_on_file_open_ = CacheMethod(env, "onFileOpen",
228                                     "(Ljava/lang/String;Ljava/lang/String;IIIZZZ)Lcom/android/"
229                                     "providers/media/FileOpenResult;");
230     mid_is_diraccess_allowed_ = CacheMethod(env, "isDirAccessAllowed", "(Ljava/lang/String;II)I");
231     mid_get_files_in_dir_ =
232             CacheMethod(env, "getFilesInDirectory", "(Ljava/lang/String;I)[Ljava/lang/String;");
233     mid_rename_ = CacheMethod(env, "rename", "(Ljava/lang/String;Ljava/lang/String;I)I");
234     mid_is_uid_allowed_access_to_data_or_obb_path_ =
235             CacheMethod(env, "isUidAllowedAccessToDataOrObbPath", "(ILjava/lang/String;)Z");
236     mid_on_file_created_ = CacheMethod(env, "onFileCreated", "(Ljava/lang/String;)V");
237     mid_should_allow_lookup_ = CacheMethod(env, "shouldAllowLookup", "(II)Z");
238     mid_is_app_clone_user_ = CacheMethod(env, "isAppCloneUser", "(I)Z");
239     mid_transform_ = CacheMethod(env, "transform", "(Ljava/lang/String;Ljava/lang/String;IIIII)Z");
240     mid_file_lookup_ =
241             CacheMethod(env, "onFileLookup",
242                         "(Ljava/lang/String;II)Lcom/android/providers/media/FileLookupResult;");
243 
244     // FileLookupResult
245     file_lookup_result_class_ = env->FindClass("com/android/providers/media/FileLookupResult");
246     if (!file_lookup_result_class_) {
247         LOG(FATAL) << "Could not find class FileLookupResult";
248     }
249     file_lookup_result_class_ =
250             reinterpret_cast<jclass>(env->NewGlobalRef(file_lookup_result_class_));
251     fid_file_lookup_transforms_ = CacheField(env, file_lookup_result_class_, "transforms", "I");
252     fid_file_lookup_transforms_reason_ =
253             CacheField(env, file_lookup_result_class_, "transformsReason", "I");
254     fid_file_lookup_uid_ = CacheField(env, file_lookup_result_class_, "uid", "I");
255     fid_file_lookup_transforms_complete_ =
256             CacheField(env, file_lookup_result_class_, "transformsComplete", "Z");
257     fid_file_lookup_transforms_supported_ =
258             CacheField(env, file_lookup_result_class_, "transformsSupported", "Z");
259     fid_file_lookup_io_path_ =
260             CacheField(env, file_lookup_result_class_, "ioPath", "Ljava/lang/String;");
261 
262     // FileOpenResult
263     file_open_result_class_ = env->FindClass("com/android/providers/media/FileOpenResult");
264     if (!file_open_result_class_) {
265         LOG(FATAL) << "Could not find class FileOpenResult";
266     }
267     file_open_result_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(file_open_result_class_));
268     fid_file_open_status_ = CacheField(env, file_open_result_class_, "status", "I");
269     fid_file_open_uid_ = CacheField(env, file_open_result_class_, "uid", "I");
270     fid_file_open_transforms_uid_ = CacheField(env, file_open_result_class_, "transformsUid", "I");
271     fid_file_open_redaction_ranges_ =
272             CacheField(env, file_open_result_class_, "redactionRanges", "[J");
273     fid_file_open_fd_ = CacheField(env, file_open_result_class_, "nativeFd", "I");
274 }
275 
~MediaProviderWrapper()276 MediaProviderWrapper::~MediaProviderWrapper() {
277     JNIEnv* env = MaybeAttachCurrentThread();
278     env->DeleteGlobalRef(media_provider_object_);
279     env->DeleteGlobalRef(media_provider_class_);
280 }
281 
InsertFile(const string & path,uid_t uid)282 int MediaProviderWrapper::InsertFile(const string& path, uid_t uid) {
283     if (uid == ROOT_UID) {
284         return 0;
285     }
286 
287     JNIEnv* env = MaybeAttachCurrentThread();
288     return insertFileInternal(env, media_provider_object_, mid_insert_file_, path, uid);
289 }
290 
DeleteFile(const string & path,uid_t uid)291 int MediaProviderWrapper::DeleteFile(const string& path, uid_t uid) {
292     if (uid == ROOT_UID) {
293         int res = unlink(path.c_str());
294         return res;
295     }
296 
297     JNIEnv* env = MaybeAttachCurrentThread();
298     return deleteFileInternal(env, media_provider_object_, mid_delete_file_, path, uid);
299 }
300 
OnFileOpen(const string & path,const string & io_path,uid_t uid,pid_t tid,int transforms_reason,bool for_write,bool redact,bool log_transforms_metrics)301 std::unique_ptr<FileOpenResult> MediaProviderWrapper::OnFileOpen(const string& path,
302                                                                  const string& io_path, uid_t uid,
303                                                                  pid_t tid, int transforms_reason,
304                                                                  bool for_write, bool redact,
305                                                                  bool log_transforms_metrics) {
306     JNIEnv* env = MaybeAttachCurrentThread();
307     if (shouldBypassMediaProvider(uid)) {
308         return std::make_unique<FileOpenResult>(0, uid, /* transforms_uid */ 0, /* nativeFd */ -1,
309                                                 new RedactionInfo());
310     }
311 
312     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
313     ScopedLocalRef<jstring> j_io_path(env, env->NewStringUTF(io_path.c_str()));
314     ScopedLocalRef<jobject> j_res_file_open_object(
315             env, env->CallObjectMethod(media_provider_object_, mid_on_file_open_, j_path.get(),
316                                        j_io_path.get(), uid, tid, transforms_reason, for_write,
317                                        redact, log_transforms_metrics));
318 
319     if (CheckForJniException(env)) {
320         return nullptr;
321     }
322 
323     const int status = env->GetIntField(j_res_file_open_object.get(), fid_file_open_status_);
324     const int original_uid = env->GetIntField(j_res_file_open_object.get(), fid_file_open_uid_);
325     const int transforms_uid =
326             env->GetIntField(j_res_file_open_object.get(), fid_file_open_transforms_uid_);
327     const int fd = env->GetIntField(j_res_file_open_object.get(), fid_file_open_fd_);
328 
329     if (redact) {
330         ScopedLocalRef<jlongArray> redaction_ranges_local_ref(
331                 env, static_cast<jlongArray>(env->GetObjectField(j_res_file_open_object.get(),
332                                                                  fid_file_open_redaction_ranges_)));
333         ScopedLongArrayRO redaction_ranges(env, redaction_ranges_local_ref.get());
334 
335         std::unique_ptr<RedactionInfo> ri;
336         if (redaction_ranges.size() % 2) {
337             LOG(ERROR) << "Error while calculating redaction ranges: array length is uneven";
338         } else if (redaction_ranges.size() > 0) {
339             ri = std::make_unique<RedactionInfo>(redaction_ranges.size() / 2,
340                                                  redaction_ranges.get());
341         } else {
342             // No ranges to redact
343             ri = std::make_unique<RedactionInfo>();
344         }
345         return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, fd,
346                                                 ri.release());
347     } else {
348         return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, fd,
349                                                 new RedactionInfo());
350     }
351 }
352 
IsCreatingDirAllowed(const string & path,uid_t uid)353 int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
354     if (shouldBypassMediaProvider(uid)) {
355         return 0;
356     }
357 
358     JNIEnv* env = MaybeAttachCurrentThread();
359     return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
360                                       uid, kCreateDirectoryRequest);
361 }
362 
IsDeletingDirAllowed(const string & path,uid_t uid)363 int MediaProviderWrapper::IsDeletingDirAllowed(const string& path, uid_t uid) {
364     if (shouldBypassMediaProvider(uid)) {
365         return 0;
366     }
367 
368     JNIEnv* env = MaybeAttachCurrentThread();
369     return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
370                                       uid, kDeleteDirectoryRequest);
371 }
372 
GetDirectoryEntries(uid_t uid,const string & path,DIR * dirp)373 std::vector<std::shared_ptr<DirectoryEntry>> MediaProviderWrapper::GetDirectoryEntries(
374         uid_t uid, const string& path, DIR* dirp) {
375     // Default value in case JNI thread was being terminated
376     std::vector<std::shared_ptr<DirectoryEntry>> res;
377     if (shouldBypassMediaProvider(uid)) {
378         addDirectoryEntriesFromLowerFs(dirp, /* filter */ nullptr, &res);
379         return res;
380     }
381 
382     JNIEnv* env = MaybeAttachCurrentThread();
383     res = getFilesInDirectoryInternal(env, media_provider_object_, mid_get_files_in_dir_, uid, path);
384 
385     const int res_size = res.size();
386     if (res_size && res[0]->d_name[0] == '/') {
387         // Path is unknown to MediaProvider, get files and directories from lower file system.
388         res.resize(0);
389         addDirectoryEntriesFromLowerFs(dirp, /* filter */ nullptr, &res);
390     } else if (res_size == 0 || !res[0]->d_name.empty()) {
391         // add directory names from lower file system.
392         addDirectoryEntriesFromLowerFs(dirp, /* filter */ &isDirectory, &res);
393     }
394     return res;
395 }
396 
IsOpendirAllowed(const string & path,uid_t uid,bool forWrite)397 int MediaProviderWrapper::IsOpendirAllowed(const string& path, uid_t uid, bool forWrite) {
398     if (shouldBypassMediaProvider(uid)) {
399         return 0;
400     }
401 
402     JNIEnv* env = MaybeAttachCurrentThread();
403     return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
404                                       uid,
405                                       forWrite ? kWriteDirectoryRequest : kReadDirectoryRequest);
406 }
407 
isUidAllowedAccessToDataOrObbPath(uid_t uid,const string & path)408 bool MediaProviderWrapper::isUidAllowedAccessToDataOrObbPath(uid_t uid, const string& path) {
409     if (shouldBypassMediaProvider(uid)) {
410         return true;
411     }
412 
413     JNIEnv* env = MaybeAttachCurrentThread();
414     return isUidAllowedAccessToDataOrObbPathInternal(
415             env, media_provider_object_, mid_is_uid_allowed_access_to_data_or_obb_path_, uid, path);
416 }
417 
Rename(const string & old_path,const string & new_path,uid_t uid)418 int MediaProviderWrapper::Rename(const string& old_path, const string& new_path, uid_t uid) {
419     // Rename from SHELL_UID should go through MediaProvider to update database rows, so only bypass
420     // MediaProvider for ROOT_UID.
421     if (uid == ROOT_UID) {
422         int res = rename(old_path.c_str(), new_path.c_str());
423         if (res != 0) res = -errno;
424         return res;
425     }
426 
427     JNIEnv* env = MaybeAttachCurrentThread();
428     return renameInternal(env, media_provider_object_, mid_rename_, old_path, new_path, uid);
429 }
430 
OnFileCreated(const string & path)431 void MediaProviderWrapper::OnFileCreated(const string& path) {
432     JNIEnv* env = MaybeAttachCurrentThread();
433 
434     return onFileCreatedInternal(env, media_provider_object_, mid_on_file_created_, path);
435 }
436 
ShouldAllowLookup(uid_t uid,int path_user_id)437 bool MediaProviderWrapper::ShouldAllowLookup(uid_t uid, int path_user_id) {
438     JNIEnv* env = MaybeAttachCurrentThread();
439 
440     bool res = env->CallBooleanMethod(media_provider_object_, mid_should_allow_lookup_, uid,
441                                       path_user_id);
442 
443     if (CheckForJniException(env)) {
444         return false;
445     }
446     return res;
447 }
448 
IsAppCloneUser(uid_t userId)449 bool MediaProviderWrapper::IsAppCloneUser(uid_t userId) {
450     JNIEnv* env = MaybeAttachCurrentThread();
451 
452     bool res = env->CallBooleanMethod(media_provider_object_, mid_is_app_clone_user_, userId);
453 
454     if (CheckForJniException(env)) {
455         return false;
456     }
457     return res;
458 }
459 
FileLookup(const std::string & path,uid_t uid,pid_t tid)460 std::unique_ptr<FileLookupResult> MediaProviderWrapper::FileLookup(const std::string& path,
461                                                                    uid_t uid, pid_t tid) {
462     JNIEnv* env = MaybeAttachCurrentThread();
463 
464     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
465 
466     ScopedLocalRef<jobject> j_res_file_lookup_object(
467             env, env->CallObjectMethod(media_provider_object_, mid_file_lookup_, j_path.get(), uid,
468                                        tid));
469 
470     if (CheckForJniException(env)) {
471         return nullptr;
472     }
473 
474     int transforms = env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_transforms_);
475     int transforms_reason =
476             env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_transforms_reason_);
477     int original_uid = env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_uid_);
478     bool transforms_complete = env->GetBooleanField(j_res_file_lookup_object.get(),
479                                                     fid_file_lookup_transforms_complete_);
480     bool transforms_supported = env->GetBooleanField(j_res_file_lookup_object.get(),
481                                                      fid_file_lookup_transforms_supported_);
482     ScopedLocalRef<jstring> j_io_path(
483             env,
484             (jstring)env->GetObjectField(j_res_file_lookup_object.get(), fid_file_lookup_io_path_));
485     ScopedUtfChars j_io_path_utf(env, j_io_path.get());
486 
487     std::unique_ptr<FileLookupResult> file_lookup_result = std::make_unique<FileLookupResult>(
488             transforms, transforms_reason, original_uid, transforms_complete, transforms_supported,
489             string(j_io_path_utf.c_str()));
490     return file_lookup_result;
491 }
492 
Transform(const std::string & src,const std::string & dst,int transforms,int transforms_reason,uid_t read_uid,uid_t open_uid,uid_t transforms_uid)493 bool MediaProviderWrapper::Transform(const std::string& src, const std::string& dst, int transforms,
494                                      int transforms_reason, uid_t read_uid, uid_t open_uid,
495                                      uid_t transforms_uid) {
496     JNIEnv* env = MaybeAttachCurrentThread();
497 
498     ScopedLocalRef<jstring> j_src(env, env->NewStringUTF(src.c_str()));
499     ScopedLocalRef<jstring> j_dst(env, env->NewStringUTF(dst.c_str()));
500     bool res = env->CallBooleanMethod(media_provider_object_, mid_transform_, j_src.get(),
501                                       j_dst.get(), transforms, transforms_reason, read_uid,
502                                       open_uid, transforms_uid);
503 
504     if (CheckForJniException(env)) {
505         return false;
506     }
507 
508     return res;
509 }
510 
511 /*****************************************************************************************/
512 /******************************** Private member functions *******************************/
513 /*****************************************************************************************/
514 
515 /**
516  * Finds MediaProvider method and adds it to methods map so it can be quickly called later.
517  */
CacheMethod(JNIEnv * env,const char method_name[],const char signature[])518 jmethodID MediaProviderWrapper::CacheMethod(JNIEnv* env, const char method_name[],
519                                             const char signature[]) {
520     jmethodID mid;
521     string actual_method_name(method_name);
522     actual_method_name.append("ForFuse");
523     mid = env->GetMethodID(media_provider_class_, actual_method_name.c_str(), signature);
524 
525     if (!mid) {
526         LOG(FATAL) << "Error caching method: " << method_name << signature;
527     }
528     return mid;
529 }
530 
DetachThreadFunction(void * unused)531 void MediaProviderWrapper::DetachThreadFunction(void* unused) {
532     int detach = gJavaVm->DetachCurrentThread();
533     CHECK_EQ(detach, 0);
534 }
535 
MaybeAttachCurrentThread()536 JNIEnv* MediaProviderWrapper::MaybeAttachCurrentThread() {
537     // We could use pthread_getspecific here as that's likely quicker but
538     // that would result in wrong behaviour for threads that don't need to
539     // be attached (e.g, those that were created in managed code).
540     JNIEnv* env = nullptr;
541     if (gJavaVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) == JNI_OK) {
542         return env;
543     }
544 
545     // This thread is currently unattached, so it must not have any TLS
546     // value. Note that we don't really care about the actual value we store
547     // in TLS -- we only care about the value destructor being called, which
548     // will happen as long as the key is not null.
549     CHECK(pthread_getspecific(gJniEnvKey) == nullptr);
550     CHECK_EQ(gJavaVm->AttachCurrentThread(&env, nullptr), 0);
551     CHECK(env != nullptr);
552 
553     pthread_setspecific(gJniEnvKey, env);
554     return env;
555 }
556 
557 }  // namespace fuse
558 }  // namespace mediaprovider
559