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