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 
validatePathIfUnicodeCheckEnabled(JNIEnv * env,jobject media_provider_object,jmethodID mid_unicode_check_enabled_,const string & path)187 int validatePathIfUnicodeCheckEnabled(JNIEnv* env, jobject media_provider_object,
188                                       jmethodID mid_unicode_check_enabled_, const string& path) {
189     bool flagEnabled = env->CallBooleanMethod(media_provider_object, mid_unicode_check_enabled_);
190     if (flagEnabled && path != env->GetStringUTFChars(env->NewStringUTF(path.c_str()), nullptr)) {
191         return EPERM;
192     }
193     return 0;
194 }
195 
onFileCreatedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_on_file_created,const string & path)196 void onFileCreatedInternal(JNIEnv* env, jobject media_provider_object,
197                            jmethodID mid_on_file_created, const string& path) {
198     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
199 
200     env->CallVoidMethod(media_provider_object, mid_on_file_created, j_path.get());
201     CheckForJniException(env);
202     return;
203 }
204 
205 }  // namespace
206 /*****************************************************************************************/
207 /******************************* Public API Implementation *******************************/
208 /*****************************************************************************************/
209 
210 JavaVM* MediaProviderWrapper::gJavaVm = nullptr;
211 pthread_key_t MediaProviderWrapper::gJniEnvKey;
212 
OneTimeInit(JavaVM * vm)213 void MediaProviderWrapper::OneTimeInit(JavaVM* vm) {
214     gJavaVm = vm;
215     CHECK(gJavaVm != nullptr);
216 
217     pthread_key_create(&MediaProviderWrapper::gJniEnvKey,
218                        MediaProviderWrapper::DetachThreadFunction);
219 }
220 
MediaProviderWrapper(JNIEnv * env,jobject media_provider)221 MediaProviderWrapper::MediaProviderWrapper(JNIEnv* env, jobject media_provider) {
222     if (!media_provider) {
223         LOG(FATAL) << "MediaProvider is null!";
224     }
225 
226     media_provider_object_ = reinterpret_cast<jobject>(env->NewGlobalRef(media_provider));
227     media_provider_class_ = env->FindClass("com/android/providers/media/MediaProvider");
228     if (!media_provider_class_) {
229         LOG(FATAL) << "Could not find class MediaProvider";
230     }
231     media_provider_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(media_provider_class_));
232 
233     // Cache methods - Before calling a method, make sure you cache it here
234     mid_insert_file_ = CacheMethod(env, "insertFileIfNecessary", "(Ljava/lang/String;I)I");
235     mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I");
236     mid_unicode_check_enabled_ = CacheMethod(env, "isUnicodeCheckEnabled", "()Z");
237     mid_on_file_open_ = CacheMethod(env, "onFileOpen",
238                                     "(Ljava/lang/String;Ljava/lang/String;IIIZZZ)Lcom/android/"
239                                     "providers/media/FileOpenResult;");
240     mid_is_diraccess_allowed_ = CacheMethod(env, "isDirAccessAllowed", "(Ljava/lang/String;II)I");
241     mid_get_files_in_dir_ =
242             CacheMethod(env, "getFilesInDirectory", "(Ljava/lang/String;I)[Ljava/lang/String;");
243     mid_rename_ = CacheMethod(env, "rename", "(Ljava/lang/String;Ljava/lang/String;I)I");
244     mid_is_uid_allowed_access_to_data_or_obb_path_ =
245             CacheMethod(env, "isUidAllowedAccessToDataOrObbPath", "(ILjava/lang/String;)Z");
246     mid_on_file_created_ = CacheMethod(env, "onFileCreated", "(Ljava/lang/String;)V");
247     mid_should_allow_lookup_ = CacheMethod(env, "shouldAllowLookup", "(II)Z");
248     mid_is_app_clone_user_ = CacheMethod(env, "isAppCloneUser", "(I)Z");
249     mid_transform_ = CacheMethod(env, "transform", "(Ljava/lang/String;Ljava/lang/String;IIIII)Z");
250     mid_file_lookup_ =
251             CacheMethod(env, "onFileLookup",
252                         "(Ljava/lang/String;II)Lcom/android/providers/media/FileLookupResult;");
253 
254     // FileLookupResult
255     file_lookup_result_class_ = env->FindClass("com/android/providers/media/FileLookupResult");
256     if (!file_lookup_result_class_) {
257         LOG(FATAL) << "Could not find class FileLookupResult";
258     }
259     file_lookup_result_class_ =
260             reinterpret_cast<jclass>(env->NewGlobalRef(file_lookup_result_class_));
261     fid_file_lookup_transforms_ = CacheField(env, file_lookup_result_class_, "transforms", "I");
262     fid_file_lookup_transforms_reason_ =
263             CacheField(env, file_lookup_result_class_, "transformsReason", "I");
264     fid_file_lookup_uid_ = CacheField(env, file_lookup_result_class_, "uid", "I");
265     fid_file_lookup_transforms_complete_ =
266             CacheField(env, file_lookup_result_class_, "transformsComplete", "Z");
267     fid_file_lookup_transforms_supported_ =
268             CacheField(env, file_lookup_result_class_, "transformsSupported", "Z");
269     fid_file_lookup_io_path_ =
270             CacheField(env, file_lookup_result_class_, "ioPath", "Ljava/lang/String;");
271 
272     // FileOpenResult
273     file_open_result_class_ = env->FindClass("com/android/providers/media/FileOpenResult");
274     if (!file_open_result_class_) {
275         LOG(FATAL) << "Could not find class FileOpenResult";
276     }
277     file_open_result_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(file_open_result_class_));
278     fid_file_open_status_ = CacheField(env, file_open_result_class_, "status", "I");
279     fid_file_open_uid_ = CacheField(env, file_open_result_class_, "uid", "I");
280     fid_file_open_transforms_uid_ = CacheField(env, file_open_result_class_, "transformsUid", "I");
281     fid_file_open_redaction_ranges_ =
282             CacheField(env, file_open_result_class_, "redactionRanges", "[J");
283     fid_file_open_fd_ = CacheField(env, file_open_result_class_, "nativeFd", "I");
284 }
285 
~MediaProviderWrapper()286 MediaProviderWrapper::~MediaProviderWrapper() {
287     JNIEnv* env = MaybeAttachCurrentThread();
288     env->DeleteGlobalRef(media_provider_object_);
289     env->DeleteGlobalRef(media_provider_class_);
290 }
291 
InsertFile(const string & path,uid_t uid)292 int MediaProviderWrapper::InsertFile(const string& path, uid_t uid) {
293     JNIEnv* env = MaybeAttachCurrentThread();
294 
295     int errCode = validatePathIfUnicodeCheckEnabled(env, media_provider_object_,
296                                                     mid_unicode_check_enabled_, path);
297     if (errCode != 0) {
298         LOG(ERROR) << "Invalid chars used in file name for creating new file";
299         return errCode;
300     }
301 
302     if (uid == ROOT_UID) {
303         return 0;
304     }
305 
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     JNIEnv* env = MaybeAttachCurrentThread();
311 
312     int errCode = validatePathIfUnicodeCheckEnabled(env, media_provider_object_,
313                                                     mid_unicode_check_enabled_, path);
314     if (errCode != 0) {
315         LOG(ERROR) << "Invalid chars used in file name while deleting file";
316         return errCode;
317     }
318 
319     if (uid == ROOT_UID) {
320         int res = unlink(path.c_str());
321         return res;
322     }
323 
324     return deleteFileInternal(env, media_provider_object_, mid_delete_file_, path, uid);
325 }
326 
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)327 std::unique_ptr<FileOpenResult> MediaProviderWrapper::OnFileOpen(const string& path,
328                                                                  const string& io_path, uid_t uid,
329                                                                  pid_t tid, int transforms_reason,
330                                                                  bool for_write, bool redact,
331                                                                  bool log_transforms_metrics) {
332     JNIEnv* env = MaybeAttachCurrentThread();
333     if (shouldBypassMediaProvider(uid)) {
334         return std::make_unique<FileOpenResult>(0, uid, /* transforms_uid */ 0, /* nativeFd */ -1,
335                                                 new RedactionInfo());
336     }
337 
338     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
339     ScopedLocalRef<jstring> j_io_path(env, env->NewStringUTF(io_path.c_str()));
340     ScopedLocalRef<jobject> j_res_file_open_object(
341             env, env->CallObjectMethod(media_provider_object_, mid_on_file_open_, j_path.get(),
342                                        j_io_path.get(), uid, tid, transforms_reason, for_write,
343                                        redact, log_transforms_metrics));
344 
345     if (CheckForJniException(env)) {
346         return nullptr;
347     }
348 
349     const int status = env->GetIntField(j_res_file_open_object.get(), fid_file_open_status_);
350     const int original_uid = env->GetIntField(j_res_file_open_object.get(), fid_file_open_uid_);
351     const int transforms_uid =
352             env->GetIntField(j_res_file_open_object.get(), fid_file_open_transforms_uid_);
353     const int fd = env->GetIntField(j_res_file_open_object.get(), fid_file_open_fd_);
354 
355     if (redact) {
356         ScopedLocalRef<jlongArray> redaction_ranges_local_ref(
357                 env, static_cast<jlongArray>(env->GetObjectField(j_res_file_open_object.get(),
358                                                                  fid_file_open_redaction_ranges_)));
359         ScopedLongArrayRO redaction_ranges(env, redaction_ranges_local_ref.get());
360 
361         std::unique_ptr<RedactionInfo> ri;
362         if (redaction_ranges.size() % 2) {
363             LOG(ERROR) << "Error while calculating redaction ranges: array length is uneven";
364         } else if (redaction_ranges.size() > 0) {
365             ri = std::make_unique<RedactionInfo>(redaction_ranges.size() / 2,
366                                                  redaction_ranges.get());
367         } else {
368             // No ranges to redact
369             ri = std::make_unique<RedactionInfo>();
370         }
371         return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, fd,
372                                                 ri.release());
373     } else {
374         return std::make_unique<FileOpenResult>(status, original_uid, transforms_uid, fd,
375                                                 new RedactionInfo());
376     }
377 }
378 
IsCreatingDirAllowed(const string & path,uid_t uid)379 int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
380     if (shouldBypassMediaProvider(uid)) {
381         return 0;
382     }
383 
384     JNIEnv* env = MaybeAttachCurrentThread();
385     return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
386                                       uid, kCreateDirectoryRequest);
387 }
388 
IsDeletingDirAllowed(const string & path,uid_t uid)389 int MediaProviderWrapper::IsDeletingDirAllowed(const string& path, uid_t uid) {
390     if (shouldBypassMediaProvider(uid)) {
391         return 0;
392     }
393 
394     JNIEnv* env = MaybeAttachCurrentThread();
395     return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
396                                       uid, kDeleteDirectoryRequest);
397 }
398 
GetDirectoryEntries(uid_t uid,const string & path,DIR * dirp)399 std::vector<std::shared_ptr<DirectoryEntry>> MediaProviderWrapper::GetDirectoryEntries(
400         uid_t uid, const string& path, DIR* dirp) {
401     // Default value in case JNI thread was being terminated
402     std::vector<std::shared_ptr<DirectoryEntry>> res;
403     if (shouldBypassMediaProvider(uid)) {
404         addDirectoryEntriesFromLowerFs(dirp, /* filter */ nullptr, &res);
405         return res;
406     }
407 
408     JNIEnv* env = MaybeAttachCurrentThread();
409     res = getFilesInDirectoryInternal(env, media_provider_object_, mid_get_files_in_dir_, uid, path);
410 
411     const int res_size = res.size();
412     if (res_size && res[0]->d_name[0] == '/') {
413         // Path is unknown to MediaProvider, get files and directories from lower file system.
414         res.resize(0);
415         addDirectoryEntriesFromLowerFs(dirp, /* filter */ nullptr, &res);
416     } else if (res_size == 0 || !res[0]->d_name.empty()) {
417         // add directory names from lower file system.
418         addDirectoryEntriesFromLowerFs(dirp, /* filter */ &isDirectory, &res);
419     }
420     return res;
421 }
422 
IsOpendirAllowed(const string & path,uid_t uid,bool forWrite)423 int MediaProviderWrapper::IsOpendirAllowed(const string& path, uid_t uid, bool forWrite) {
424     if (shouldBypassMediaProvider(uid)) {
425         return 0;
426     }
427 
428     JNIEnv* env = MaybeAttachCurrentThread();
429     return isDirAccessAllowedInternal(env, media_provider_object_, mid_is_diraccess_allowed_, path,
430                                       uid,
431                                       forWrite ? kWriteDirectoryRequest : kReadDirectoryRequest);
432 }
433 
isUidAllowedAccessToDataOrObbPath(uid_t uid,const string & path)434 bool MediaProviderWrapper::isUidAllowedAccessToDataOrObbPath(uid_t uid, const string& path) {
435     if (shouldBypassMediaProvider(uid)) {
436         return true;
437     }
438 
439     JNIEnv* env = MaybeAttachCurrentThread();
440     return isUidAllowedAccessToDataOrObbPathInternal(
441             env, media_provider_object_, mid_is_uid_allowed_access_to_data_or_obb_path_, uid, path);
442 }
443 
Rename(const string & old_path,const string & new_path,uid_t uid)444 int MediaProviderWrapper::Rename(const string& old_path, const string& new_path, uid_t uid) {
445     // Rename from SHELL_UID should go through MediaProvider to update database rows, so only bypass
446     // MediaProvider for ROOT_UID.
447     JNIEnv* env = MaybeAttachCurrentThread();
448     int errCodeForOldPath = validatePathIfUnicodeCheckEnabled(env, media_provider_object_,
449                                                               mid_unicode_check_enabled_, old_path);
450     if (errCodeForOldPath != 0) {
451         LOG(ERROR) << "Invalid chars used in old file name";
452         return errCodeForOldPath;
453     }
454 
455     int errCodeForNewPath = validatePathIfUnicodeCheckEnabled(env, media_provider_object_,
456                                                               mid_unicode_check_enabled_, new_path);
457     if (errCodeForNewPath != 0) {
458         LOG(ERROR) << "Invalid chars used in new file name";
459         return errCodeForNewPath;
460     }
461 
462     if (uid == ROOT_UID) {
463         int res = rename(old_path.c_str(), new_path.c_str());
464         if (res != 0) res = -errno;
465         return res;
466     }
467 
468     return renameInternal(env, media_provider_object_, mid_rename_, old_path, new_path, uid);
469 }
470 
OnFileCreated(const string & path)471 void MediaProviderWrapper::OnFileCreated(const string& path) {
472     JNIEnv* env = MaybeAttachCurrentThread();
473 
474     return onFileCreatedInternal(env, media_provider_object_, mid_on_file_created_, path);
475 }
476 
ShouldAllowLookup(uid_t uid,int path_user_id)477 bool MediaProviderWrapper::ShouldAllowLookup(uid_t uid, int path_user_id) {
478     JNIEnv* env = MaybeAttachCurrentThread();
479 
480     bool res = env->CallBooleanMethod(media_provider_object_, mid_should_allow_lookup_, uid,
481                                       path_user_id);
482 
483     if (CheckForJniException(env)) {
484         return false;
485     }
486     return res;
487 }
488 
IsAppCloneUser(uid_t userId)489 bool MediaProviderWrapper::IsAppCloneUser(uid_t userId) {
490     JNIEnv* env = MaybeAttachCurrentThread();
491 
492     bool res = env->CallBooleanMethod(media_provider_object_, mid_is_app_clone_user_, userId);
493 
494     if (CheckForJniException(env)) {
495         return false;
496     }
497     return res;
498 }
499 
FileLookup(const std::string & path,uid_t uid,pid_t tid)500 std::unique_ptr<FileLookupResult> MediaProviderWrapper::FileLookup(const std::string& path,
501                                                                    uid_t uid, pid_t tid) {
502     JNIEnv* env = MaybeAttachCurrentThread();
503 
504     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
505 
506     ScopedLocalRef<jobject> j_res_file_lookup_object(
507             env, env->CallObjectMethod(media_provider_object_, mid_file_lookup_, j_path.get(), uid,
508                                        tid));
509 
510     if (CheckForJniException(env)) {
511         return nullptr;
512     }
513 
514     int transforms = env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_transforms_);
515     int transforms_reason =
516             env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_transforms_reason_);
517     int original_uid = env->GetIntField(j_res_file_lookup_object.get(), fid_file_lookup_uid_);
518     bool transforms_complete = env->GetBooleanField(j_res_file_lookup_object.get(),
519                                                     fid_file_lookup_transforms_complete_);
520     bool transforms_supported = env->GetBooleanField(j_res_file_lookup_object.get(),
521                                                      fid_file_lookup_transforms_supported_);
522     ScopedLocalRef<jstring> j_io_path(
523             env,
524             (jstring)env->GetObjectField(j_res_file_lookup_object.get(), fid_file_lookup_io_path_));
525     ScopedUtfChars j_io_path_utf(env, j_io_path.get());
526 
527     std::unique_ptr<FileLookupResult> file_lookup_result = std::make_unique<FileLookupResult>(
528             transforms, transforms_reason, original_uid, transforms_complete, transforms_supported,
529             string(j_io_path_utf.c_str()));
530     return file_lookup_result;
531 }
532 
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)533 bool MediaProviderWrapper::Transform(const std::string& src, const std::string& dst, int transforms,
534                                      int transforms_reason, uid_t read_uid, uid_t open_uid,
535                                      uid_t transforms_uid) {
536     JNIEnv* env = MaybeAttachCurrentThread();
537 
538     ScopedLocalRef<jstring> j_src(env, env->NewStringUTF(src.c_str()));
539     ScopedLocalRef<jstring> j_dst(env, env->NewStringUTF(dst.c_str()));
540     bool res = env->CallBooleanMethod(media_provider_object_, mid_transform_, j_src.get(),
541                                       j_dst.get(), transforms, transforms_reason, read_uid,
542                                       open_uid, transforms_uid);
543 
544     if (CheckForJniException(env)) {
545         return false;
546     }
547 
548     return res;
549 }
550 
551 /*****************************************************************************************/
552 /******************************** Private member functions *******************************/
553 /*****************************************************************************************/
554 
555 /**
556  * Finds MediaProvider method and adds it to methods map so it can be quickly called later.
557  */
CacheMethod(JNIEnv * env,const char method_name[],const char signature[])558 jmethodID MediaProviderWrapper::CacheMethod(JNIEnv* env, const char method_name[],
559                                             const char signature[]) {
560     jmethodID mid;
561     string actual_method_name(method_name);
562     actual_method_name.append("ForFuse");
563     mid = env->GetMethodID(media_provider_class_, actual_method_name.c_str(), signature);
564 
565     if (!mid) {
566         LOG(FATAL) << "Error caching method: " << method_name << signature;
567     }
568     return mid;
569 }
570 
DetachThreadFunction(void * unused)571 void MediaProviderWrapper::DetachThreadFunction(void* unused) {
572     int detach = gJavaVm->DetachCurrentThread();
573     CHECK_EQ(detach, 0);
574 }
575 
MaybeAttachCurrentThread()576 JNIEnv* MediaProviderWrapper::MaybeAttachCurrentThread() {
577     // We could use pthread_getspecific here as that's likely quicker but
578     // that would result in wrong behaviour for threads that don't need to
579     // be attached (e.g, those that were created in managed code).
580     JNIEnv* env = nullptr;
581     if (gJavaVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) == JNI_OK) {
582         return env;
583     }
584 
585     // This thread is currently unattached, so it must not have any TLS
586     // value. Note that we don't really care about the actual value we store
587     // in TLS -- we only care about the value destructor being called, which
588     // will happen as long as the key is not null.
589     CHECK(pthread_getspecific(gJniEnvKey) == nullptr);
590     CHECK_EQ(gJavaVm->AttachCurrentThread(&env, nullptr), 0);
591     CHECK(env != nullptr);
592 
593     pthread_setspecific(gJniEnvKey, env);
594     return env;
595 }
596 
597 }  // namespace fuse
598 }  // namespace mediaprovider
599