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