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