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_setup_public_volume_db_backup(JNIEnv * env,jobject self,jlong java_daemon,jstring volume_name)199 void com_android_providers_media_FuseDaemon_setup_public_volume_db_backup(JNIEnv* env, jobject self,
200 jlong java_daemon,
201 jstring volume_name) {
202 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
203 ScopedUtfChars utf_chars_volumeName(env, volume_name);
204 if (!utf_chars_volumeName.c_str()) {
205 LOG(WARNING) << "Couldn't initialise FUSE device id for " << volume_name;
206 return;
207 }
208 daemon->SetupPublicVolumeLevelDbInstance(utf_chars_volumeName.c_str());
209 }
210
com_android_providers_media_FuseDaemon_delete_db_backup(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)211 void com_android_providers_media_FuseDaemon_delete_db_backup(JNIEnv* env, jobject self,
212 jlong java_daemon, jstring java_path) {
213 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
214 ScopedUtfChars utf_chars_path(env, java_path);
215 if (!utf_chars_path.c_str()) {
216 LOG(WARNING) << "Couldn't initialise FUSE device id";
217 return;
218 }
219 daemon->DeleteFromLevelDb(utf_chars_path.c_str());
220 }
221
com_android_providers_media_FuseDaemon_backup_volume_db_data(JNIEnv * env,jobject self,jlong java_daemon,jstring volume_name,jstring java_path,jstring value)222 void com_android_providers_media_FuseDaemon_backup_volume_db_data(JNIEnv* env, jobject self,
223 jlong java_daemon,
224 jstring volume_name,
225 jstring java_path,
226 jstring value) {
227 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
228 ScopedUtfChars utf_chars_path(env, java_path);
229 ScopedUtfChars utf_chars_value(env, value);
230 ScopedUtfChars utf_chars_volumeName(env, volume_name);
231 if (!utf_chars_path.c_str()) {
232 LOG(WARNING) << "Couldn't initialise FUSE device id";
233 return;
234 }
235 daemon->InsertInLevelDb(utf_chars_volumeName.c_str(), utf_chars_path.c_str(),
236 utf_chars_value.c_str());
237 }
238
com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv * env,jclass clazz)239 bool com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv* env, jclass clazz) {
240 return pthread_getspecific(fuse::MediaProviderWrapper::gJniEnvKey) != nullptr;
241 }
242
com_android_providers_media_FuseDaemon_read_backed_up_file_paths(JNIEnv * env,jobject self,jlong java_daemon,jstring volumeName,jstring lastReadValue,jint limit)243 jobjectArray com_android_providers_media_FuseDaemon_read_backed_up_file_paths(
244 JNIEnv* env, jobject self, jlong java_daemon, jstring volumeName, jstring lastReadValue,
245 jint limit) {
246 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
247 ScopedUtfChars utf_chars_volumeName(env, volumeName);
248 ScopedUtfChars utf_chars_lastReadValue(env, lastReadValue);
249 if (!utf_chars_volumeName.c_str()) {
250 LOG(WARNING) << "Couldn't initialise FUSE device id";
251 return nullptr;
252 }
253 return convert_string_vector_to_object_array(
254 env, daemon->ReadFilePathsFromLevelDb(utf_chars_volumeName.c_str(),
255 utf_chars_lastReadValue.c_str(), limit));
256 }
257
com_android_providers_media_FuseDaemon_read_backed_up_data(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)258 jstring com_android_providers_media_FuseDaemon_read_backed_up_data(JNIEnv* env, jobject self,
259 jlong java_daemon,
260 jstring java_path) {
261 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
262 ScopedUtfChars utf_chars_path(env, java_path);
263 if (!utf_chars_path.c_str()) {
264 LOG(WARNING) << "Couldn't initialise FUSE device id";
265 return nullptr;
266 }
267 return env->NewStringUTF(daemon->ReadBackedUpDataFromLevelDb(utf_chars_path.c_str()).c_str());
268 }
269
com_android_providers_media_FuseDaemon_read_ownership(JNIEnv * env,jobject self,jlong java_daemon,jstring key)270 jstring com_android_providers_media_FuseDaemon_read_ownership(JNIEnv* env, jobject self,
271 jlong java_daemon, jstring key) {
272 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
273 ScopedUtfChars utf_chars_key(env, key);
274 return env->NewStringUTF(daemon->ReadOwnership(utf_chars_key.c_str()).c_str());
275 }
276
com_android_providers_media_FuseDaemon_create_owner_id_relation(JNIEnv * env,jobject self,jlong java_daemon,jstring owner_id,jstring owner_pkg_identifier)277 void com_android_providers_media_FuseDaemon_create_owner_id_relation(JNIEnv* env, jobject self,
278 jlong java_daemon,
279 jstring owner_id,
280 jstring owner_pkg_identifier) {
281 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
282 ScopedUtfChars utf_chars_owner_id(env, owner_id);
283 ScopedUtfChars utf_chars_owner_pkg_identifier(env, owner_pkg_identifier);
284 daemon->CreateOwnerIdRelation(utf_chars_owner_id.c_str(),
285 utf_chars_owner_pkg_identifier.c_str());
286 }
287
com_android_providers_media_FuseDaemon_read_owner_relations(JNIEnv * env,jobject self,jlong java_daemon)288 jobject com_android_providers_media_FuseDaemon_read_owner_relations(JNIEnv* env, jobject self,
289 jlong java_daemon) {
290 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
291 // Create a Java map object.
292 jobject map =
293 env->NewObject(env->FindClass("java/util/HashMap"),
294 env->GetMethodID(env->FindClass("java/util/HashMap"), "<init>", "()V"));
295
296 // Get the key-value pairs from the native method.
297 std::map<std::string, std::string> myMap = daemon->GetOwnerRelationship();
298
299 // Iterate over the map and add the key-value pairs to the Java map.
300 for (auto it = myMap.begin(); it != myMap.end(); ++it) {
301 env->CallObjectMethod(
302 map,
303 env->GetMethodID(env->FindClass("java/util/HashMap"), "put",
304 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"),
305 env->NewStringUTF(it->first.c_str()), env->NewStringUTF(it->second.c_str()));
306 }
307
308 // Return the Java map object.
309 return map;
310 }
311
com_android_providers_media_FuseDaemon_remove_owner_id_relation(JNIEnv * env,jobject self,jlong java_daemon,jstring owner_id,jstring owner_pkg_identifier)312 void com_android_providers_media_FuseDaemon_remove_owner_id_relation(JNIEnv* env, jobject self,
313 jlong java_daemon,
314 jstring owner_id,
315 jstring owner_pkg_identifier) {
316 fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
317 ScopedUtfChars utf_chars_owner_id(env, owner_id);
318 ScopedUtfChars utf_chars_owner_pkg_identifier(env, owner_pkg_identifier);
319 daemon->RemoveOwnerIdRelation(utf_chars_owner_id.c_str(),
320 utf_chars_owner_pkg_identifier.c_str());
321 }
322
323 const JNINativeMethod methods[] = {
324 {"native_new", "(Lcom/android/providers/media/MediaProvider;)J",
325 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_new)},
326 {"native_start", "(JILjava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;)V",
327 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_start)},
328 {"native_delete", "(J)V",
329 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)},
330 {"native_should_open_with_fuse", "(JLjava/lang/String;ZI)Z",
331 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_should_open_with_fuse)},
332 {"native_uses_fuse_passthrough", "(J)Z",
333 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_uses_fuse_passthrough)},
334 {"native_is_fuse_thread", "()Z",
335 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_fuse_thread)},
336 {"native_is_started", "(J)Z",
337 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_started)},
338 {"native_invalidate_fuse_dentry_cache", "(JLjava/lang/String;)V",
339 reinterpret_cast<void*>(
340 com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache)},
341 {"native_check_fd_access", "(JII)Lcom/android/providers/media/FdAccessResult;",
342 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_check_fd_access)},
343 {"native_initialize_device_id", "(JLjava/lang/String;)V",
344 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_initialize_device_id)},
345 {"native_setup_volume_db_backup", "(J)V",
346 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_setup_volume_db_backup)},
347 {"native_setup_public_volume_db_backup", "(JLjava/lang/String;)V",
348 reinterpret_cast<void*>(
349 com_android_providers_media_FuseDaemon_setup_public_volume_db_backup)},
350 {"native_delete_db_backup", "(JLjava/lang/String;)V",
351 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete_db_backup)},
352 {"native_backup_volume_db_data",
353 "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
354 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_backup_volume_db_data)},
355 {"native_read_backed_up_file_paths",
356 "(JLjava/lang/String;Ljava/lang/String;I)[Ljava/lang/String;",
357 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_backed_up_file_paths)},
358 {"native_read_backed_up_data", "(JLjava/lang/String;)Ljava/lang/String;",
359 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_backed_up_data)},
360 {"native_read_ownership", "(JLjava/lang/String;)Ljava/lang/String;",
361 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_ownership)},
362 {"native_create_owner_id_relation", "(JLjava/lang/String;Ljava/lang/String;)V",
363 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_create_owner_id_relation)},
364 {"native_read_owner_relations", "(J)Ljava/util/HashMap;",
365 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_read_owner_relations)},
366 {"native_remove_owner_id_relation", "(JLjava/lang/String;Ljava/lang/String;)V",
367 reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_remove_owner_id_relation)}};
368 } // namespace
369
register_android_providers_media_FuseDaemon(JavaVM * vm,JNIEnv * env)370 void register_android_providers_media_FuseDaemon(JavaVM* vm, JNIEnv* env) {
371 gFuseDaemonClass =
372 static_cast<jclass>(env->NewGlobalRef(env->FindClass(FUSE_DAEMON_CLASS_NAME)));
373 gFdAccessResultClass =
374 static_cast<jclass>(env->NewGlobalRef(env->FindClass(FD_ACCESS_RESULT_CLASS_NAME)));
375
376 if (gFuseDaemonClass == nullptr) {
377 LOG(FATAL) << "Unable to find class : " << FUSE_DAEMON_CLASS_NAME;
378 }
379
380 if (gFdAccessResultClass == nullptr) {
381 LOG(FATAL) << "Unable to find class : " << FD_ACCESS_RESULT_CLASS_NAME;
382 }
383
384 if (env->RegisterNatives(gFuseDaemonClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
385 LOG(FATAL) << "Unable to register native methods";
386 }
387
388 gFdAccessResultCtor = env->GetMethodID(gFdAccessResultClass, "<init>", "(Ljava/lang/String;Z)V");
389 if (gFdAccessResultCtor == nullptr) {
390 LOG(FATAL) << "Unable to find ctor for FdAccessResult";
391 }
392
393 fuse::MediaProviderWrapper::OneTimeInit(vm);
394 }
395 } // namespace mediaprovider
396