Merge changes I1a2b9958,I534ac743,I9e50bb3a into rvc-dev

* changes:
  Update database for apps bypassing scoped storage restrictions
  Grant concession for FileManager and SystemGallery
  Insert database row for apps bypassing scoped storage
diff --git a/deploy.sh b/deploy.sh
index 22524c0..e6edcce 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -8,6 +8,7 @@
 adb remount
 adb sync
 adb shell umount /apex/com.android.mediaprovider*
+adb shell setprop apexd.status '""'
 adb shell setprop ctl.restart apexd
 adb shell rm -rf /system/priv-app/MediaProvider
 adb shell rm -rf /system/priv-app/MediaProviderGoogle
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index 63015bd..7f95186 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -1554,7 +1554,7 @@
     return active.load(std::memory_order_acquire);
 }
 
-void FuseDaemon::Start(const int fd, const std::string& path) {
+void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path) {
     struct fuse_args args;
     struct fuse_cmdline_opts opts;
 
diff --git a/jni/FuseDaemon.h b/jni/FuseDaemon.h
index d9925d4..3c4b947 100644
--- a/jni/FuseDaemon.h
+++ b/jni/FuseDaemon.h
@@ -20,6 +20,8 @@
 #include <memory>
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 #include "MediaProviderWrapper.h"
 #include "jni.h"
 
@@ -35,7 +37,7 @@
     /**
      * Start the FUSE daemon loop that will handle filesystem calls.
      */
-    void Start(const int fd, const std::string& path);
+    void Start(android::base::unique_fd fd, const std::string& path);
 
     /**
      * Checks if the FUSE daemon is started.
diff --git a/jni/com_android_providers_media_FuseDaemon.cpp b/jni/com_android_providers_media_FuseDaemon.cpp
index ae1c175..3a65696 100644
--- a/jni/com_android_providers_media_FuseDaemon.cpp
+++ b/jni/com_android_providers_media_FuseDaemon.cpp
@@ -24,6 +24,7 @@
 #include "FuseDaemon.h"
 #include "MediaProviderWrapper.h"
 #include "android-base/logging.h"
+#include "android-base/unique_fd.h"
 
 namespace mediaprovider {
 namespace {
@@ -42,12 +43,14 @@
     LOG(DEBUG) << "Starting the FUSE daemon...";
     fuse::FuseDaemon* const daemon = reinterpret_cast<fuse::FuseDaemon*>(java_daemon);
 
+    android::base::unique_fd ufd(fd);
+
     ScopedUtfChars utf_chars_path(env, java_path);
     if (!utf_chars_path.c_str()) {
         return;
     }
 
-    daemon->Start(fd, utf_chars_path.c_str());
+    daemon->Start(std::move(ufd), utf_chars_path.c_str());
 }
 
 bool com_android_providers_media_FuseDaemon_is_started(JNIEnv* env, jobject self,
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 3265df1..952f895 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -4019,19 +4019,19 @@
      */
     private int deleteRecursive(SQLiteQueryBuilder qb, DatabaseHelper helper, String userWhere,
             String[] userWhereArgs) {
-        synchronized (mDirectoryCache) {
-            mDirectoryCache.clear();
+        return (int) helper.runWithTransaction((db) -> {
+            synchronized (mDirectoryCache) {
+                mDirectoryCache.clear();
+            }
 
-            return (int) helper.runWithTransaction((db) -> {
-                int n = 0;
-                int total = 0;
-                do {
-                    n = qb.delete(helper, userWhere, userWhereArgs);
-                    total += n;
-                } while (n > 0);
-                return total;
-            });
-        }
+            int n = 0;
+            int total = 0;
+            do {
+                n = qb.delete(helper, userWhere, userWhereArgs);
+                total += n;
+            } while (n > 0);
+            return total;
+        });
     }
 
     @Override
@@ -4384,7 +4384,7 @@
             // doesn't exist we fall through to create it below
             final File thumbFile = getThumbnailFile(uri);
             try {
-                return ParcelFileDescriptor.open(thumbFile,
+                return FileUtils.openSafely(thumbFile,
                         ParcelFileDescriptor.MODE_READ_ONLY);
             } catch (FileNotFoundException ignored) {
             }
@@ -4407,9 +4407,9 @@
                 // once for remote reading. Both FDs point at the same
                 // underlying inode on disk, so they're stable across renames
                 // to avoid race conditions between threads.
-                thumbWrite = ParcelFileDescriptor.open(thumbTempFile,
+                thumbWrite = FileUtils.openSafely(thumbTempFile,
                         ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
-                thumbRead = ParcelFileDescriptor.open(thumbTempFile,
+                thumbRead = FileUtils.openSafely(thumbTempFile,
                         ParcelFileDescriptor.MODE_READ_ONLY);
 
                 final Bitmap thumbnail = getThumbnailBitmap(uri, signal);
@@ -4828,15 +4828,6 @@
         // Make sure any updated paths look sane
         assertFileColumnsSane(match, uri, initialValues);
 
-        // if the media type is being changed, check if it's being changed from image or video
-        // to something else
-        if (initialValues.containsKey(FileColumns.MEDIA_TYPE)) {
-            final int newMediaType = initialValues.getAsInteger(FileColumns.MEDIA_TYPE);
-
-            // If we're changing media types, invalidate any thumbnails
-            triggerInvalidate = true;
-        }
-
         if (initialValues.containsKey(FileColumns.DATA)) {
             // If we're changing paths, invalidate any thumbnails
             triggerInvalidate = true;
@@ -4861,11 +4852,7 @@
 
         final ContentValues values = new ContentValues(initialValues);
         switch (match) {
-            case AUDIO_MEDIA_ID: {
-                computeAudioLocalizedValues(values);
-                computeAudioKeyValues(values);
-                // fall-through
-            }
+            case AUDIO_MEDIA_ID:
             case AUDIO_PLAYLISTS_ID:
             case VIDEO_MEDIA_ID:
             case IMAGES_MEDIA_ID:
@@ -4876,6 +4863,17 @@
             }
         }
 
+        if (initialValues.containsKey(FileColumns.MEDIA_TYPE)) {
+            final int mediaType = initialValues.getAsInteger(FileColumns.MEDIA_TYPE);
+            switch (mediaType) {
+                case FileColumns.MEDIA_TYPE_AUDIO: {
+                    computeAudioLocalizedValues(values);
+                    computeAudioKeyValues(values);
+                    break;
+                }
+            }
+        }
+
         count = qb.update(helper, values, userWhere, userWhereArgs);
 
         // If the caller tried (and failed) to update metadata, the file on disk
@@ -5520,7 +5518,7 @@
                     // If fuse is enabled, we can provide an fd that points to the fuse
                     // file system and handle redaction in the fuse handler when the caller reads.
                     Log.i(TAG, "Redacting with new FUSE for " + filePath);
-                    pfd = ParcelFileDescriptor.open(getFuseFile(file), modeBits);
+                    pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
                 } else {
                     // TODO(b/135341978): Remove this and associated code
                     // when fuse is on by default.
@@ -5538,7 +5536,7 @@
                     daemon = getFuseDaemonForFile(file);
                 } catch (FileNotFoundException ignored) {
                 }
-                ParcelFileDescriptor lowerFsFd = ParcelFileDescriptor.open(file, modeBits);
+                ParcelFileDescriptor lowerFsFd = FileUtils.openSafely(file, modeBits);
                 boolean forRead = (modeBits & ParcelFileDescriptor.MODE_READ_ONLY) != 0;
                 boolean shouldOpenWithFuse = daemon != null
                         && daemon.shouldOpenWithFuse(filePath, forRead, lowerFsFd.getFd());
@@ -5549,7 +5547,7 @@
                     // resulting from cache inconsistencies between the upper and lower
                     // filesystem caches
                     Log.w(TAG, "Using FUSE for " + filePath);
-                    pfd = ParcelFileDescriptor.open(getFuseFile(file), modeBits);
+                    pfd = FileUtils.openSafely(getFuseFile(file), modeBits);
                     try {
                         lowerFsFd.close();
                     } catch (IOException e) {
diff --git a/src/com/android/providers/media/fuse/FuseDaemon.java b/src/com/android/providers/media/fuse/FuseDaemon.java
index 95a160b..3b7a0f8 100644
--- a/src/com/android/providers/media/fuse/FuseDaemon.java
+++ b/src/com/android/providers/media/fuse/FuseDaemon.java
@@ -151,7 +151,10 @@
     }
 
     private native long native_new(MediaProvider mediaProvider);
+
+    // Takes ownership of the passed in file descriptor!
     private native void native_start(long daemon, int deviceFd, String path);
+
     private native void native_delete(long daemon);
     private native boolean native_should_open_with_fuse(long daemon, String path, boolean readLock,
             int fd);
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 6b0cd80..e711d0c 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -25,12 +25,16 @@
 import static android.system.OsConstants.F_OK;
 import static android.system.OsConstants.O_ACCMODE;
 import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CLOEXEC;
 import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_NOFOLLOW;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_RDWR;
 import static android.system.OsConstants.O_TRUNC;
 import static android.system.OsConstants.O_WRONLY;
 import static android.system.OsConstants.R_OK;
+import static android.system.OsConstants.S_IRWXG;
+import static android.system.OsConstants.S_IRWXU;
 import static android.system.OsConstants.W_OK;
 
 import static com.android.providers.media.util.DatabaseUtils.getAsBoolean;
@@ -43,9 +47,13 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Environment;
+import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -56,6 +64,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -80,10 +89,39 @@
 import java.util.regex.Pattern;
 
 public class FileUtils {
+    /**
+     * Drop-in replacement for {@link ParcelFileDescriptor#open(File, int)}
+     * which adds security features like {@link OsConstants#O_CLOEXEC} and
+     * {@link OsConstants#O_NOFOLLOW}.
+     */
+    public static @NonNull ParcelFileDescriptor openSafely(@NonNull File file, int pfdFlags)
+            throws FileNotFoundException {
+        final int posixFlags = translateModePfdToPosix(pfdFlags) | O_CLOEXEC | O_NOFOLLOW;
+        try {
+            final FileDescriptor fd = Os.open(file.getAbsolutePath(), posixFlags,
+                    S_IRWXU | S_IRWXG);
+            try {
+                return ParcelFileDescriptor.dup(fd);
+            } finally {
+                closeQuietly(fd);
+            }
+        } catch (IOException | ErrnoException e) {
+            throw new FileNotFoundException(e.getMessage());
+        }
+    }
+
     public static void closeQuietly(@Nullable AutoCloseable closeable) {
         android.os.FileUtils.closeQuietly(closeable);
     }
 
+    public static void closeQuietly(@Nullable FileDescriptor fd) {
+        if (fd == null) return;
+        try {
+            Os.close(fd);
+        } catch (ErrnoException ignored) {
+        }
+    }
+
     public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
         return android.os.FileUtils.copy(in, out);
     }
diff --git a/src/com/android/providers/media/util/RedactingFileDescriptor.java b/src/com/android/providers/media/util/RedactingFileDescriptor.java
index a0bec59..bf2524f 100644
--- a/src/com/android/providers/media/util/RedactingFileDescriptor.java
+++ b/src/com/android/providers/media/util/RedactingFileDescriptor.java
@@ -47,15 +47,10 @@
     private volatile long[] mRedactRanges;
     private volatile long[] mFreeOffsets;
 
-    private FileDescriptor mInner = null;
+    private ParcelFileDescriptor mInner = null;
     private ParcelFileDescriptor mOuter = null;
 
-    public static void closeQuietly(FileDescriptor fd) {
-        try {
-            Os.close(fd);
-        } catch (ErrnoException ignored) {
-        }
-    }
+    private FileDescriptor mInnerFd = null;
 
     private RedactingFileDescriptor(
             Context context, File file, int mode, long[] redactRanges, long[] freeOffsets)
@@ -64,17 +59,13 @@
         mFreeOffsets = freeOffsets;
 
         try {
-            try {
-                mInner = Os.open(file.getAbsolutePath(),
-                        FileUtils.translateModePfdToPosix(mode), 0);
-                mOuter = context.getSystemService(StorageManager.class)
-                        .openProxyFileDescriptor(mode, mCallback,
-                                new Handler(Looper.getMainLooper()));
-            } catch (ErrnoException e) {
-                throw e.rethrowAsIOException();
-            }
+            mInner = FileUtils.openSafely(file, mode);
+            mInnerFd = mInner.getFileDescriptor();
+            mOuter = context.getSystemService(StorageManager.class)
+                    .openProxyFileDescriptor(mode, mCallback,
+                            new Handler(Looper.getMainLooper()));
         } catch (IOException e) {
-            closeQuietly(mInner);
+            FileUtils.closeQuietly(mInner);
             FileUtils.closeQuietly(mOuter);
             throw e;
         }
@@ -158,7 +149,7 @@
     private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
         @Override
         public long onGetSize() throws ErrnoException {
-            return Os.fstat(mInner).st_size;
+            return Os.fstat(mInnerFd).st_size;
         }
 
         @Override
@@ -166,7 +157,7 @@
             int n = 0;
             while (n < size) {
                 try {
-                    final int res = Os.pread(mInner, data, n, size - n, offset + n);
+                    final int res = Os.pread(mInnerFd, data, n, size - n, offset + n);
                     if (res == 0) {
                         break;
                     } else {
@@ -203,7 +194,7 @@
             int n = 0;
             while (n < size) {
                 try {
-                    final int res = Os.pwrite(mInner, data, n, size - n, offset + n);
+                    final int res = Os.pwrite(mInnerFd, data, n, size - n, offset + n);
                     if (res == 0) {
                         break;
                     } else {
@@ -222,12 +213,12 @@
 
         @Override
         public void onFsync() throws ErrnoException {
-            Os.fsync(mInner);
+            Os.fsync(mInnerFd);
         }
 
         @Override
         public void onRelease() {
-            closeQuietly(mInner);
+            FileUtils.closeQuietly(mInner);
         }
     };
 }