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 // Need to use LOGE_EX.
18 #define LOG_TAG "FuseDaemonJNI"
19
20 #include <nativehelper/scoped_local_ref.h>
21 #include <nativehelper/scoped_utf_chars.h>
22
23 #include <string>
24 #include <vector>
25
26 #include "FuseDaemon.h"
27 #include "MediaProviderWrapper.h"
28 #include "android-base/logging.h"
29 #include "android-base/unique_fd.h"
30
31 namespace mediaprovider {
32 namespace {
33
34 constexpr const char* FUSE_DAEMON_CLASS_NAME = "com/android/providers/media/fuse/FuseDaemon";
35 constexpr const char* FD_ACCESS_RESULT_CLASS_NAME = "com/android/providers/media/FdAccessResult";
36 static jclass gFuseDaemonClass;
37 static jclass gFdAccessResultClass;
38 static jmethodID gFdAccessResultCtor;
39
convert_object_array_to_string_vector(JNIEnv * env,jobjectArray java_object_array,const std::string & element_description)40 static std::vector<std::string> convert_object_array_to_string_vector(
41 JNIEnv* env, jobjectArray java_object_array, const std::string& element_description) {
42 ScopedLocalRef<jobjectArray> j_ref_object_array(env, java_object_array);
43 std::vector<std::string> utf_strings;
44
45 const int object_array_length = env->GetArrayLength(j_ref_object_array.get());
46 for (int i = 0; i < object_array_length; i++) {
47 ScopedLocalRef<jstring> j_ref_string(
48 env, (jstring)env->GetObjectArrayElement(j_ref_object_array.get(), i));
49 ScopedUtfChars utf_chars(env, j_ref_string.get());
50 const char* utf_string = utf_chars.c_str();
51
52 if (utf_string) {
53 utf_strings.push_back(utf_string);
54 } else {
55 LOG(ERROR) << "Error reading " << element_description << " at index: " << i;
56 }
57 }
58
59 return utf_strings;
60 }
61
convert_string_vector_to_object_array(JNIEnv * env,std::vector<std::string> string_vector)62 static jobjectArray convert_string_vector_to_object_array(JNIEnv* env,
63 std::vector<std::string> string_vector) {
64 jclass stringClass = env->FindClass("java/lang/String");
65 jobjectArray arr = env->NewObjectArray(string_vector.size(), stringClass, NULL);
66 for (int i = 0; i < string_vector.size(); i++) {
67 ScopedLocalRef<jstring> path(env, env->NewStringUTF(string_vector.at(i).c_str()));
68 env->SetObjectArrayElement(arr, i, path.get());
69 }
70 return arr;
71 }
72
get_supported_transcoding_relative_paths(JNIEnv * env,jobjectArray java_supported_transcoding_relative_paths)73 static std::vector<std::string> get_supported_transcoding_relative_paths(
74 JNIEnv* env, jobjectArray java_supported_transcoding_relative_paths) {
75 return convert_object_array_to_string_vector(env, java_supported_transcoding_relative_paths,
76 "supported transcoding relative path");
77 }
78
get_supported_uncached_relative_paths(JNIEnv * env,jobjectArray java_supported_uncached_relative_paths)79 static std::vector<std::string> get_supported_uncached_relative_paths(
80 JNIEnv* env, jobjectArray java_supported_uncached_relative_paths) {
81 return convert_object_array_to_string_vector(env, java_supported_uncached_relative_paths,
82 "supported uncached relative path");
83 }
84
com_android_providers_media_FuseDaemon_new(JNIEnv * env,jobject self,jobject media_provider)85 jlong com_android_providers_media_FuseDaemon_new(JNIEnv* env, jobject self,
86 jobject media_provider) {
87 LOG(DEBUG) << "Creating the FUSE daemon...";
88 return reinterpret_cast<jlong>(new fuse::FuseDaemon(env, media_provider));
89 }
90
com_android_providers_media_FuseDaemon_start(JNIEnv * env,jobject self,jlong java_daemon,jint fd,jstring java_path,jboolean uncached_mode,jobjectArray java_supported_transcoding_relative_paths,jobjectArray java_supported_uncached_relative_paths)91 void com_android_providers_media_FuseDaemon_start(
92 JNIEnv* env, jobject self, jlong java_daemon, jint fd, jstring java_path,
93 jboolean uncached_mode, jobjectArray java_supported_transcoding_relative_paths,
94 jobjectArray java_supported_uncached_relative_paths) {
95 LOG(DEBUG) << "Starting the FUSE daemon...";
96 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
97
98 android::base::unique_fd ufd(fd);
99
100 ScopedUtfChars utf_chars_path(env, java_path);
101 if (!utf_chars_path.c_str()) {
102 return;
103 }
104
105 const std::vector<std::string>& transcoding_relative_paths =
106 get_supported_transcoding_relative_paths(env,
107 java_supported_transcoding_relative_paths);
108 const std::vector<std::string>& uncached_relative_paths =
109 get_supported_uncached_relative_paths(env, java_supported_uncached_relative_paths);
110
111 daemon->Start(std::move(ufd), utf_chars_path.c_str(), uncached_mode, transcoding_relative_paths,
112 uncached_relative_paths);
113 }
114
com_android_providers_media_FuseDaemon_is_started(JNIEnv * env,jobject self,jlong java_daemon)115 bool com_android_providers_media_FuseDaemon_is_started(JNIEnv* env, jobject self,
116 jlong java_daemon) {
117 LOG(DEBUG) << "Checking if FUSE daemon started...";
118 const fuse::FuseDaemon* daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
119 return daemon->IsStarted();
120 }
121
com_android_providers_media_FuseDaemon_delete(JNIEnv * env,jobject self,jlong java_daemon)122 void com_android_providers_media_FuseDaemon_delete(JNIEnv* env, jobject self, jlong java_daemon) {
123 LOG(DEBUG) << "Destroying the FUSE daemon...";
124 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
125 delete daemon;
126 }
127
com_android_providers_media_FuseDaemon_should_open_with_fuse(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path,jboolean for_read,jint fd)128 jboolean com_android_providers_media_FuseDaemon_should_open_with_fuse(JNIEnv* env, jobject self,
129 jlong java_daemon,
130 jstring java_path,
131 jboolean for_read, jint fd) {
132 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
133 if (daemon) {
134 ScopedUtfChars utf_chars_path(env, java_path);
135 if (!utf_chars_path.c_str()) {
136 // TODO(b/145741852): Throw exception
137 return JNI_FALSE;
138 }
139
140 return daemon->ShouldOpenWithFuse(fd, for_read, utf_chars_path.c_str());
141 }
142 // TODO(b/145741852): Throw exception
143 return JNI_FALSE;
144 }
145
com_android_providers_media_FuseDaemon_uses_fuse_passthrough(JNIEnv * env,jobject self,jlong java_daemon)146 jboolean com_android_providers_media_FuseDaemon_uses_fuse_passthrough(JNIEnv* env, jobject self,
147 jlong java_daemon) {
148 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
149 if (daemon) {
150 return daemon->UsesFusePassthrough();
151 }
152 return JNI_FALSE;
153 }
154
com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)155 void com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache(JNIEnv* env, jobject self,
156 jlong java_daemon,
157 jstring java_path) {
158 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
159 if (daemon) {
160 ScopedUtfChars utf_chars_path(env, java_path);
161 if (!utf_chars_path.c_str()) {
162 // TODO(b/145741152): Throw exception
163 return;
164 }
165
166 CHECK(pthread_getspecific(fuse::MediaProviderWrapper::gJniEnvKey) == nullptr);
167 daemon->InvalidateFuseDentryCache(utf_chars_path.c_str());
168 }
169 // TODO(b/145741152): Throw exception
170 }
171
com_android_providers_media_FuseDaemon_check_fd_access(JNIEnv * env,jobject self,jlong java_daemon,jint fd,jint uid)172 jobject com_android_providers_media_FuseDaemon_check_fd_access(JNIEnv* env, jobject self,
173 jlong java_daemon, jint fd,
174 jint uid) {
175 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
176 const std::unique_ptr<fuse::FdAccessResult> result = daemon->CheckFdAccess(fd, uid);
177 return env->NewObject(gFdAccessResultClass, gFdAccessResultCtor,
178 env->NewStringUTF(result->file_path.c_str()), result->should_redact);
179 }
180
com_android_providers_media_FuseDaemon_initialize_device_id(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)181 void com_android_providers_media_FuseDaemon_initialize_device_id(JNIEnv* env, jobject self,
182 jlong java_daemon,
183 jstring java_path) {
184 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
185 ScopedUtfChars utf_chars_path(env, java_path);
186 if (!utf_chars_path.c_str()) {
187 LOG(WARNING) << "Couldn't initialise FUSE device id";
188 return;
189 }
190 daemon->InitializeDeviceId(utf_chars_path.c_str());
191 }
192
com_android_providers_media_FuseDaemon_setup_volume_db_backup(JNIEnv * env,jobject self,jlong java_daemon)193 void com_android_providers_media_FuseDaemon_setup_volume_db_backup(JNIEnv* env, jobject self,
194 jlong java_daemon) {
195 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
196 daemon->SetupLevelDbInstances();
197 }
198
com_android_providers_media_FuseDaemon_delete_db_backup(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)199 void com_android_providers_media_FuseDaemon_delete_db_backup(JNIEnv* env, jobject self,
200 jlong java_daemon, jstring java_path) {
201 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
202 ScopedUtfChars utf_chars_path(env, java_path);
203 if (!utf_chars_path.c_str()) {
204 LOG(WARNING) << "Couldn't initialise FUSE device id";
205 return;
206 }
207 daemon->DeleteFromLevelDb(utf_chars_path.c_str());
208 }
209
com_android_providers_media_FuseDaemon_backup_volume_db_data(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path,jstring value)210 void com_android_providers_media_FuseDaemon_backup_volume_db_data(JNIEnv* env, jobject self,
211 jlong java_daemon,
212 jstring java_path, jstring value) {
213 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
214 ScopedUtfChars utf_chars_path(env, java_path);
215 ScopedUtfChars utf_chars_value(env, value);
216 if (!utf_chars_path.c_str()) {
217 LOG(WARNING) << "Couldn't initialise FUSE device id";
218 return;
219 }
220 daemon->InsertInLevelDb(utf_chars_path.c_str(), utf_chars_value.c_str());
221 }
222
com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv * env,jclass clazz)223 bool com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv* env, jclass clazz) {
224 return pthread_getspecific(fuse::MediaProviderWrapper::gJniEnvKey) != nullptr;
225 }
226
com_android_providers_media_FuseDaemon_read_backed_up_file_paths(JNIEnv * env,jobject self,jlong java_daemon,jstring volumeName,jstring lastReadValue,jint limit)227 jobjectArray com_android_providers_media_FuseDaemon_read_backed_up_file_paths(
228 JNIEnv* env, jobject self, jlong java_daemon, jstring volumeName, jstring lastReadValue,
229 jint limit) {
230 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
231 ScopedUtfChars utf_chars_volumeName(env, volumeName);
232 ScopedUtfChars utf_chars_lastReadValue(env, lastReadValue);
233 if (!utf_chars_volumeName.c_str()) {
234 LOG(WARNING) << "Couldn't initialise FUSE device id";
235 return nullptr;
236 }
237 return convert_string_vector_to_object_array(
238 env, daemon->ReadFilePathsFromLevelDb(utf_chars_volumeName.c_str(),
239 utf_chars_lastReadValue.c_str(), limit));
240 }
241
com_android_providers_media_FuseDaemon_read_backed_up_data(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)242 jstring com_android_providers_media_FuseDaemon_read_backed_up_data(JNIEnv* env, jobject self,
243 jlong java_daemon,
244 jstring java_path) {
245 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
246 ScopedUtfChars utf_chars_path(env, java_path);
247 if (!utf_chars_path.c_str()) {
248 LOG(WARNING) << "Couldn't initialise FUSE device id";
249 return nullptr;
250 }
251 return env->NewStringUTF(daemon->ReadBackedUpDataFromLevelDb(utf_chars_path.c_str()).c_str());
252 }
253
com_android_providers_media_FuseDaemon_read_ownership(JNIEnv * env,jobject self,jlong java_daemon,jstring key)254 jstring com_android_providers_media_FuseDaemon_read_ownership(JNIEnv* env, jobject self,
255 jlong java_daemon, jstring key) {
256 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
257 ScopedUtfChars utf_chars_key(env, key);
258 return env->NewStringUTF(daemon->ReadOwnership(utf_chars_key.c_str()).c_str());
259 }
260
com_android_providers_media_FuseDaemon_create_owner_id_relation(JNIEnv * env,jobject self,jlong java_daemon,jstring owner_id,jstring owner_pkg_identifier)261 void com_android_providers_media_FuseDaemon_create_owner_id_relation(JNIEnv* env, jobject self,
262 jlong java_daemon,
263 jstring owner_id,
264 jstring owner_pkg_identifier) {
265 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
266 ScopedUtfChars utf_chars_owner_id(env, owner_id);
267 ScopedUtfChars utf_chars_owner_pkg_identifier(env, owner_pkg_identifier);
268 daemon->CreateOwnerIdRelation(utf_chars_owner_id.c_str(),
269 utf_chars_owner_pkg_identifier.c_str());
270 }
271
com_android_providers_media_FuseDaemon_read_owner_relations(JNIEnv * env,jobject self,jlong java_daemon)272 jobject com_android_providers_media_FuseDaemon_read_owner_relations(JNIEnv* env, jobject self,
273 jlong java_daemon) {
274 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
275 // Create a Java map object.
276 jobject map =
277 env->NewObject(env->FindClass("java/util/HashMap"),
278 env->GetMethodID(env->FindClass("java/util/HashMap"), "<init>", "()V"));
279
280 // Get the key-value pairs from the native method.
281 std::map<std::string, std::string> myMap = daemon->GetOwnerRelationship();
282
283 // Iterate over the map and add the key-value pairs to the Java map.
284 for (auto it = myMap.begin(); it != myMap.end(); ++it) {
285 env->CallObjectMethod(
286 map,
287 env->GetMethodID(env->FindClass("java/util/HashMap"), "put",
288 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"),
289 env->NewStringUTF(it->first.c_str()), env->NewStringUTF(it->second.c_str()));
290 }
291
292 // Return the Java map object.
293 return map;
294 }
295
com_android_providers_media_FuseDaemon_remove_owner_id_relation(JNIEnv * env,jobject self,jlong java_daemon,jstring owner_id,jstring owner_pkg_identifier)296 void com_android_providers_media_FuseDaemon_remove_owner_id_relation(JNIEnv* env, jobject self,
297 jlong java_daemon,
298 jstring owner_id,
299 jstring owner_pkg_identifier) {
300 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
301 ScopedUtfChars utf_chars_owner_id(env, owner_id);
302 ScopedUtfChars utf_chars_owner_pkg_identifier(env, owner_pkg_identifier);
303 daemon->RemoveOwnerIdRelation(utf_chars_owner_id.c_str(),
304 utf_chars_owner_pkg_identifier.c_str());
305 }
306
307 const JNINativeMethod methods[] = {
308 {"native_new", "(Lcom/android/providers/media/MediaProvider;)J",
309 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_new)},
310 {"native_start", "(JILjava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;)V",
311 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_start)},
312 {"native_delete", "(J)V",
313 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)},
314 {"native_should_open_with_fuse", "(JLjava/lang/String;ZI)Z",
315 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_should_open_with_fuse)},
316 {"native_uses_fuse_passthrough", "(J)Z",
317 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_uses_fuse_passthrough)},
318 {"native_is_fuse_thread", "()Z",
319 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_fuse_thread)},
320 {"native_is_started", "(J)Z",
321 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_started)},
322 {"native_invalidate_fuse_dentry_cache", "(JLjava/lang/String;)V",
323 reinterpret_cast<void*>(
324 com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache)},
325 {"native_check_fd_access", "(JII)Lcom/android/providers/media/FdAccessResult;",
326 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_check_fd_access)},
327 {"native_initialize_device_id", "(JLjava/lang/String;)V",
328 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_initialize_device_id)},
329 {"native_setup_volume_db_backup", "(J)V",
330 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_setup_volume_db_backup)},
331 {"native_delete_db_backup", "(JLjava/lang/String;)V",
332 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete_db_backup)},
333 {"native_backup_volume_db_data", "(JLjava/lang/String;Ljava/lang/String;)V",
334 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_backup_volume_db_data)},
335 {"native_read_backed_up_file_paths",
336 "(JLjava/lang/String;Ljava/lang/String;I)[Ljava/lang/String;",
337 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_backed_up_file_paths)},
338 {"native_read_backed_up_data", "(JLjava/lang/String;)Ljava/lang/String;",
339 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_backed_up_data)},
340 {"native_read_ownership", "(JLjava/lang/String;)Ljava/lang/String;",
341 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_ownership)},
342 {"native_create_owner_id_relation", "(JLjava/lang/String;Ljava/lang/String;)V",
343 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_create_owner_id_relation)},
344 {"native_read_owner_relations", "(J)Ljava/util/HashMap;",
345 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_owner_relations)},
346 {"native_remove_owner_id_relation", "(JLjava/lang/String;Ljava/lang/String;)V",
347 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_remove_owner_id_relation)}};
348 } // namespace
349
register_android_providers_media_FuseDaemon(JavaVM * vm,JNIEnv * env)350 void register_android_providers_media_FuseDaemon(JavaVM* vm, JNIEnv* env) {
351 gFuseDaemonClass =
352 static_cast<jclass>(env->NewGlobalRef(env->FindClass(FUSE_DAEMON_CLASS_NAME)));
353 gFdAccessResultClass =
354 static_cast<jclass>(env->NewGlobalRef(env->FindClass(FD_ACCESS_RESULT_CLASS_NAME)));
355
356 if (gFuseDaemonClass == nullptr) {
357 LOG(FATAL) << "Unable to find class : " << FUSE_DAEMON_CLASS_NAME;
358 }
359
360 if (gFdAccessResultClass == nullptr) {
361 LOG(FATAL) << "Unable to find class : " << FD_ACCESS_RESULT_CLASS_NAME;
362 }
363
364 if (env->RegisterNatives(gFuseDaemonClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
365 LOG(FATAL) << "Unable to register native methods";
366 }
367
368 gFdAccessResultCtor = env->GetMethodID(gFdAccessResultClass, "<init>", "(Ljava/lang/String;Z)V");
369 if (gFdAccessResultCtor == nullptr) {
370 LOG(FATAL) << "Unable to find ctor for FdAccessResult";
371 }
372
373 fuse::MediaProviderWrapper::OneTimeInit(vm);
374 }
375 } // namespace mediaprovider
376