Add ExternalStorageServiceImpl to android.process.media

The impl uses the API added in
I9b132ce7e5e5985ef3307c75ce7db50affc65a8e

At the moment, this impl only supports handling filesystem IO using
FUSE and runs a FUSE daemon in the media process, FuseDaemon.

The service will handle filesytem IO from an upper file system mounted
for apps on /sdcard. It will receive these requests on /dev/fuse and
respond using the data in the MediaProvider database that has already
indexed the appropriate lower filesystem e.g /data/media/0

Bug: 135341433
Test: m
Change-Id: I722e79cc9eff31944ba37942411b3d8b68d195f5
diff --git a/jni/com_android_providers_media_FuseDaemon.cpp b/jni/com_android_providers_media_FuseDaemon.cpp
new file mode 100644
index 0000000..175d49c
--- /dev/null
+++ b/jni/com_android_providers_media_FuseDaemon.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+// Need to use LOGE_EX.
+#define LOG_TAG "FuseDaemon"
+
+#include "FuseDaemon.h"
+
+#include <string>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_utf_chars.h>
+#include <cutils/log.h>
+
+namespace mediaprovider {
+namespace {
+
+constexpr const char* CLASS_NAME = "com/android/providers/media/fuse/FuseDaemon";
+static jclass gFuseDaemonClass;
+
+jlong com_android_providers_media_FuseDaemon_new(JNIEnv* env, jobject self,
+        jobject mediaProvider) {
+    ALOGD("Creating the FUSE daemon...\n");
+    return reinterpret_cast<jlong>(new fuse::FuseDaemon(env, mediaProvider));
+}
+
+void com_android_providers_media_FuseDaemon_start(
+        JNIEnv* env, jobject self, jlong java_daemon, jint fd, jstring java_upper_path,
+        jstring java_lower_path) {
+    ALOGD("Starting the FUSE daemon...");
+    fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
+
+    ScopedUtfChars utf_chars_upper_path(env, java_upper_path);
+    if (!utf_chars_upper_path.c_str()) {
+        return;
+    }
+    const std::string& string_upper_path = std::string(utf_chars_upper_path.c_str());
+
+    ScopedUtfChars utf_chars_lower_path(env, java_lower_path);
+    if (!utf_chars_lower_path.c_str()) {
+        return;
+    }
+    const std::string& string_lower_path = std::string(utf_chars_lower_path.c_str());
+
+    daemon->Start(fd, string_upper_path, string_lower_path);
+}
+
+void com_android_providers_media_FuseDaemon_stop(
+        JNIEnv* env, jobject self, jlong java_daemon) {
+    ALOGD("Stopping the FUSE daemon...");
+    fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
+    daemon->Stop();
+}
+
+void com_android_providers_media_FuseDaemon_delete(
+        JNIEnv* env, jobject self, jlong java_daemon) {
+    ALOGD("Destroying the FUSE daemon...");
+    fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
+    delete daemon;
+}
+
+const JNINativeMethod methods[] = {
+    {
+        "native_new",
+        "(Lcom/android/providers/media/MediaProvider;)J",
+        reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_new)
+    },
+    {
+        "native_start",
+        "(JILjava/lang/String;Ljava/lang/String;)V",
+        reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_start)
+    },
+    {
+        "native_stop",
+        "(J)V",
+        reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_stop)
+    },
+    {
+        "native_delete",
+        "(J)V",
+        reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)
+    }
+};
+}  // namespace
+
+void register_android_providers_media_FuseDaemon(JNIEnv* env) {
+    gFuseDaemonClass = static_cast<jclass>(env->NewGlobalRef(env->FindClass(CLASS_NAME)));
+    jniRegisterNativeMethods(env, CLASS_NAME, methods, NELEM(methods));
+}
+}  // namespace mediaprovider