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