• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 #include "FuseDaemon.h"
26 #include "MediaProviderWrapper.h"
27 #include "android-base/logging.h"
28 #include "android-base/unique_fd.h"
29 
30 namespace mediaprovider {
31 namespace {
32 
33 constexpr const char* FUSE_DAEMON_CLASS_NAME = "com/android/providers/media/fuse/FuseDaemon";
34 constexpr const char* FD_ACCESS_RESULT_CLASS_NAME = "com/android/providers/media/FdAccessResult";
35 static jclass gFuseDaemonClass;
36 static jclass gFdAccessResultClass;
37 static jmethodID gFdAccessResultCtor;
38 
convert_object_array_to_string_vector(JNIEnv * env,jobjectArray java_object_array,const std::string & element_description)39 static std::vector<std::string> convert_object_array_to_string_vector(
40         JNIEnv* env, jobjectArray java_object_array, const std::string& element_description) {
41     ScopedLocalRef<jobjectArray> j_ref_object_array(env, java_object_array);
42     std::vector<std::string> utf_strings;
43 
44     const int object_array_length = env->GetArrayLength(j_ref_object_array.get());
45     for (int i = 0; i < object_array_length; i++) {
46         ScopedLocalRef<jstring> j_ref_string(
47                 env, (jstring)env->GetObjectArrayElement(j_ref_object_array.get(), i));
48         ScopedUtfChars utf_chars(env, j_ref_string.get());
49         const char* utf_string = utf_chars.c_str();
50 
51         if (utf_string) {
52             utf_strings.push_back(utf_string);
53         } else {
54             LOG(ERROR) << "Error reading " << element_description << " at index: " << i;
55         }
56     }
57 
58     return utf_strings;
59 }
60 
get_supported_transcoding_relative_paths(JNIEnv * env,jobjectArray java_supported_transcoding_relative_paths)61 static std::vector<std::string> get_supported_transcoding_relative_paths(
62         JNIEnv* env, jobjectArray java_supported_transcoding_relative_paths) {
63     return convert_object_array_to_string_vector(env, java_supported_transcoding_relative_paths,
64                                                  "supported transcoding relative path");
65 }
66 
get_supported_uncached_relative_paths(JNIEnv * env,jobjectArray java_supported_uncached_relative_paths)67 static std::vector<std::string> get_supported_uncached_relative_paths(
68         JNIEnv* env, jobjectArray java_supported_uncached_relative_paths) {
69     return convert_object_array_to_string_vector(env, java_supported_uncached_relative_paths,
70                                                  "supported uncached relative path");
71 }
72 
com_android_providers_media_FuseDaemon_new(JNIEnv * env,jobject self,jobject media_provider)73 jlong com_android_providers_media_FuseDaemon_new(JNIEnv* env, jobject self,
74                                                  jobject media_provider) {
75     LOG(DEBUG) << "Creating the FUSE daemon...";
76     return reinterpret_cast<jlong>(new fuse::FuseDaemon(env, media_provider));
77 }
78 
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)79 void com_android_providers_media_FuseDaemon_start(
80         JNIEnv* env, jobject self, jlong java_daemon, jint fd, jstring java_path,
81         jboolean uncached_mode, jobjectArray java_supported_transcoding_relative_paths,
82         jobjectArray java_supported_uncached_relative_paths) {
83     LOG(DEBUG) << "Starting the FUSE daemon...";
84     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
85 
86     android::base::unique_fd ufd(fd);
87 
88     ScopedUtfChars utf_chars_path(env, java_path);
89     if (!utf_chars_path.c_str()) {
90         return;
91     }
92 
93     const std::vector<std::string>& transcoding_relative_paths =
94             get_supported_transcoding_relative_paths(env,
95                     java_supported_transcoding_relative_paths);
96     const std::vector<std::string>& uncached_relative_paths =
97             get_supported_uncached_relative_paths(env, java_supported_uncached_relative_paths);
98 
99     daemon->Start(std::move(ufd), utf_chars_path.c_str(), uncached_mode, transcoding_relative_paths,
100                   uncached_relative_paths);
101 }
102 
com_android_providers_media_FuseDaemon_is_started(JNIEnv * env,jobject self,jlong java_daemon)103 bool com_android_providers_media_FuseDaemon_is_started(JNIEnv* env, jobject self,
104                                                        jlong java_daemon) {
105     LOG(DEBUG) << "Checking if FUSE daemon started...";
106     const fuse::FuseDaemon* daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
107     return daemon->IsStarted();
108 }
109 
com_android_providers_media_FuseDaemon_delete(JNIEnv * env,jobject self,jlong java_daemon)110 void com_android_providers_media_FuseDaemon_delete(JNIEnv* env, jobject self, jlong java_daemon) {
111     LOG(DEBUG) << "Destroying the FUSE daemon...";
112     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
113     delete daemon;
114 }
115 
com_android_providers_media_FuseDaemon_should_open_with_fuse(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path,jboolean for_read,jint fd)116 jboolean com_android_providers_media_FuseDaemon_should_open_with_fuse(JNIEnv* env, jobject self,
117                                                                       jlong java_daemon,
118                                                                       jstring java_path,
119                                                                       jboolean for_read, jint fd) {
120     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
121     if (daemon) {
122         ScopedUtfChars utf_chars_path(env, java_path);
123         if (!utf_chars_path.c_str()) {
124             // TODO(b/145741852): Throw exception
125             return JNI_FALSE;
126         }
127 
128         return daemon->ShouldOpenWithFuse(fd, for_read, utf_chars_path.c_str());
129     }
130     // TODO(b/145741852): Throw exception
131     return JNI_FALSE;
132 }
133 
com_android_providers_media_FuseDaemon_uses_fuse_passthrough(JNIEnv * env,jobject self,jlong java_daemon)134 jboolean com_android_providers_media_FuseDaemon_uses_fuse_passthrough(JNIEnv* env, jobject self,
135                                                                       jlong java_daemon) {
136     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
137     if (daemon) {
138         return daemon->UsesFusePassthrough();
139     }
140     return JNI_FALSE;
141 }
142 
com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)143 void com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache(JNIEnv* env, jobject self,
144                                                                          jlong java_daemon,
145                                                                          jstring java_path) {
146     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
147     if (daemon) {
148         ScopedUtfChars utf_chars_path(env, java_path);
149         if (!utf_chars_path.c_str()) {
150             // TODO(b/145741152): Throw exception
151             return;
152         }
153 
154         CHECK(pthread_getspecific(fuse::MediaProviderWrapper::gJniEnvKey) == nullptr);
155         daemon->InvalidateFuseDentryCache(utf_chars_path.c_str());
156     }
157     // TODO(b/145741152): Throw exception
158 }
159 
com_android_providers_media_FuseDaemon_check_fd_access(JNIEnv * env,jobject self,jlong java_daemon,jint fd,jint uid)160 jobject com_android_providers_media_FuseDaemon_check_fd_access(JNIEnv* env, jobject self,
161                                                                jlong java_daemon, jint fd,
162                                                                jint uid) {
163     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
164     const std::unique_ptr<fuse::FdAccessResult> result = daemon->CheckFdAccess(fd, uid);
165     return env->NewObject(gFdAccessResultClass, gFdAccessResultCtor,
166                           env->NewStringUTF(result->file_path.c_str()), result->should_redact);
167 }
168 
com_android_providers_media_FuseDaemon_initialize_device_id(JNIEnv * env,jobject self,jlong java_daemon,jstring java_path)169 void com_android_providers_media_FuseDaemon_initialize_device_id(JNIEnv* env, jobject self,
170                                                                  jlong java_daemon,
171                                                                  jstring java_path) {
172     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
173     ScopedUtfChars utf_chars_path(env, java_path);
174     if (!utf_chars_path.c_str()) {
175         LOG(WARNING) << "Couldn't initialise FUSE device id";
176         return;
177     }
178     daemon->InitializeDeviceId(utf_chars_path.c_str());
179 }
180 
com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv * env,jclass clazz)181 bool com_android_providers_media_FuseDaemon_is_fuse_thread(JNIEnv* env, jclass clazz) {
182     return pthread_getspecific(fuse::MediaProviderWrapper::gJniEnvKey) != nullptr;
183 }
184 
185 const JNINativeMethod methods[] = {
186         {"native_new", "(Lcom/android/providers/media/MediaProvider;)J",
187          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_new)},
188         {"native_start", "(JILjava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;)V",
189          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_start)},
190         {"native_delete", "(J)V",
191          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)},
192         {"native_should_open_with_fuse", "(JLjava/lang/String;ZI)Z",
193          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_should_open_with_fuse)},
194         {"native_uses_fuse_passthrough", "(J)Z",
195          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_uses_fuse_passthrough)},
196         {"native_is_fuse_thread", "()Z",
197          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_fuse_thread)},
198         {"native_is_started", "(J)Z",
199          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_is_started)},
200         {"native_invalidate_fuse_dentry_cache", "(JLjava/lang/String;)V",
201          reinterpret_cast<void*>(
202                  com_android_providers_media_FuseDaemon_invalidate_fuse_dentry_cache)},
203         {"native_check_fd_access", "(JII)Lcom/android/providers/media/FdAccessResult;",
204          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_check_fd_access)},
205         {"native_initialize_device_id", "(JLjava/lang/String;)V",
206          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_initialize_device_id)}};
207 }  // namespace
208 
register_android_providers_media_FuseDaemon(JavaVM * vm,JNIEnv * env)209 void register_android_providers_media_FuseDaemon(JavaVM* vm, JNIEnv* env) {
210     gFuseDaemonClass =
211             static_cast<jclass>(env->NewGlobalRef(env->FindClass(FUSE_DAEMON_CLASS_NAME)));
212     gFdAccessResultClass =
213             static_cast<jclass>(env->NewGlobalRef(env->FindClass(FD_ACCESS_RESULT_CLASS_NAME)));
214 
215     if (gFuseDaemonClass == nullptr) {
216         LOG(FATAL) << "Unable to find class : " << FUSE_DAEMON_CLASS_NAME;
217     }
218 
219     if (gFdAccessResultClass == nullptr) {
220         LOG(FATAL) << "Unable to find class : " << FD_ACCESS_RESULT_CLASS_NAME;
221     }
222 
223     if (env->RegisterNatives(gFuseDaemonClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
224         LOG(FATAL) << "Unable to register native methods";
225     }
226 
227     gFdAccessResultCtor = env->GetMethodID(gFdAccessResultClass, "<init>", "(Ljava/lang/String;Z)V");
228     if (gFdAccessResultCtor == nullptr) {
229         LOG(FATAL) << "Unable to find ctor for FdAccessResult";
230     }
231 
232     fuse::MediaProviderWrapper::OneTimeInit(vm);
233 }
234 }  // namespace mediaprovider
235