Reland: resolve FUSE and lower FS cache inconsistencies

Rf - Read FUSE, Wf - Write FUSE
Rl - Read lower fs, Wl - Write lower fs

File reads in the following scenarios may not see the latest data
because of different VFS caches:
1. Rf - Wl -Rf
2. Rl - Wf - Rl

To fix the scenarios above we dynamically return a FUSE or lower FS fd
from the MediaProvider#open or open with or without caching during
FUSE_OPEN. Here are the conditions:

MP open:
- Return FUSE fd if file already opened with caching via FUSE or we are
  unable to SET_OFD_SETLK to F_RDLCK or F_WRLCK with fcntl(2).
- Return lower FS fd otherwise

FUSE open:
- Open with caching if F_OFD_GETLK with fcntl(2) returns F_UNLCK
- Open with direct_io otherwise

Unfortunately, we cannot place a F_RDLCK on a file opened in write
only mode and we cannot place a F_WRLCK twice. This means that if a
file is opened via MP in write only mode, we can only set a F_WRLCK
and subsequent opens will have to go through FUSE.

Pseudo code of algorithm implemented:

MP#opeFd
  lowerFsFd = open(lowerPath)
  useFuse = FuseD#shouldOpenWithFuse(lowerPath, fd)
  if useFuse
    upperFsFd = open(upperPath)
    return upperFsFd
  else
    return lowerFsFd

FuseD#open
  lowerFsFd = open(path)
  hasLock = fcntl(get_lock, lowerFsFd)
  if hasLock
    return uncachedFileInfo
  else
    return cachedFileInfo

FuseD#shouldOpenWithFuse
  if cached
    return true
  else
    res = fcntl(set_read_or_write_lock, fd)
    return res

Test: atest FuseDaemonHostTest#testVfsCacheConsistency
Test: atest FuseDaemonHostTest
Test: adb logcat -v color | grep --color=never -iE "using fuse|using lower|using direct|using cache"
Change-Id: I7726a75a51869c0e3ea3856103dd501b1aa19d14
FuseDaemon: [/storage/emulated] Using cache for /storage/emulated/0/DCIM/open_file_path_write_content_resolver.jpg
MediaProvider: Using FUSE for /storage/emulated/0/DCIM/open_file_path_write_content_resolver.jpg
FuseDaemon: [/storage/emulated] Using cache for /storage/emulated/0/DCIM/open_file_path_write_content_resolver.jpg
MediaProvider: Using lower FS for /storage/emulated/0/DCIM/open_content_resolver_write_content_resolver.jpg
FuseDaemon: [/storage/emulated] Using direct io for /storage/emulated/0/DCIM/open_content_resolver_write_content_resolver.jpg
FuseDaemon: [/storage/emulated] Using cache for /storage/emulated/0/DCIM/open_file_path_write_file_path.jpg
MediaProvider: Using FUSE for /storage/emulated/0/DCIM/open_file_path_write_file_path.jpg
FuseDaemon: [/storage/emulated] Using cache for /storage/emulated/0/DCIM/open_file_path_write_file_path.jpg
MediaProvider: Using lower FS for /storage/emulated/0/DCIM/open_content_resolver_write_file_path.jpg
FuseDaemon: [/storage/emulated] Using direct io for /storage/emulated/0/DCIM/open_content_resolver_write_file_path.jpg
MediaProvider: Using lower FS for /storage/emulated/0/DCIM/open_content_resolver_write_only.jpg
MediaProvider: Using FUSE for /storage/emulated/0/DCIM/open_content_resolver_write_only.jpg
FuseDaemon: [/storage/emulated] Using direct io for /storage/emulated/0/DCIM/open_content_resolver_write_only.jpg
MediaProvider: Using lower FS for /storage/emulated/0/DCIM/open_content_resolver_dup.jpg
FuseDaemon: [/storage/emulated] Using direct io for /storage/emulated/0/DCIM/open_content_resolver_dup.jpg
Bug: 135341433
diff --git a/jni/com_android_providers_media_FuseDaemon.cpp b/jni/com_android_providers_media_FuseDaemon.cpp
index 1633b99..caf00b2 100644
--- a/jni/com_android_providers_media_FuseDaemon.cpp
+++ b/jni/com_android_providers_media_FuseDaemon.cpp
@@ -30,9 +30,10 @@
 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) {
+jlong com_android_providers_media_FuseDaemon_new(JNIEnv* env, jobject self,
+                                                 jobject media_provider) {
     LOG(DEBUG) << "Creating the FUSE daemon...";
-    return reinterpret_cast<jlong>(new fuse::FuseDaemon(env, mediaProvider));
+    return reinterpret_cast<jlong>(new fuse::FuseDaemon(env, media_provider));
 }
 
 void com_android_providers_media_FuseDaemon_start(JNIEnv* env, jobject self, jlong java_daemon,
@@ -44,9 +45,8 @@
     if (!utf_chars_path.c_str()) {
         return;
     }
-    const std::string& string_path = std::string(utf_chars_path.c_str());
 
-    daemon->Start(fd, string_path);
+    daemon->Start(fd, utf_chars_path.c_str());
 }
 
 void com_android_providers_media_FuseDaemon_delete(JNIEnv* env, jobject self, jlong java_daemon) {
@@ -55,13 +55,34 @@
     delete daemon;
 }
 
+jboolean com_android_providers_media_FuseDaemon_should_open_with_fuse(JNIEnv* env, jobject self,
+                                                                      jlong java_daemon,
+                                                                      jstring java_path,
+                                                                      jboolean for_read, jint fd) {
+    fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
+    if (daemon) {
+        ScopedUtfChars utf_chars_path(env, java_path);
+        if (!utf_chars_path.c_str()) {
+            // TODO(b/145741852): Throw exception
+            return JNI_FALSE;
+        }
+
+        return daemon->ShouldOpenWithFuse(fd, for_read, utf_chars_path.c_str());
+    }
+    // TODO(b/145741852): Throw exception
+    return JNI_FALSE;
+}
+
 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;)V",
          reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_start)},
         {"native_delete", "(J)V",
-         reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)}};
+         reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_delete)},
+        {"native_should_open_with_fuse", "(JLjava/lang/String;ZI)Z",
+         reinterpret_cast<void*>(com_android_providers_media_FuseDaemon_should_open_with_fuse)}};
+
 }  // namespace
 
 void register_android_providers_media_FuseDaemon(JNIEnv* env) {