Merge "Ensure MediaProvider Fuse is ready after Vold reset"
diff --git a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
index a93aeee..549179d 100644
--- a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
+++ b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
@@ -50,6 +50,7 @@
 import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.provider.MediaStore;
 
 import androidx.annotation.Nullable;
@@ -155,10 +156,11 @@
         try {
             final String mode = queryType.equals(IS_URI_REDACTED_VIA_FILE_DESCRIPTOR_FOR_WRITE)
                     ? "w" : "r";
-            FileDescriptor fd = getContentResolver().openFileDescriptor(uri,
-                    mode).getFileDescriptor();
-            ExifInterface exifInterface = new ExifInterface(fd);
-            intent.putExtra(queryType, exifInterface.getGpsDateTime() == -1);
+            try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, mode)) {
+                FileDescriptor fd = pfd.getFileDescriptor();
+                ExifInterface exifInterface = new ExifInterface(fd);
+                intent.putExtra(queryType, exifInterface.getGpsDateTime() == -1);
+            }
         } catch (Exception e) {
             intent.putExtra(INTENT_EXCEPTION, e);
         }
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java
index f0be82f..a122110 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java
@@ -50,6 +50,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
 import android.provider.MediaStore;
 
 import androidx.test.filters.SdkSuppress;
@@ -449,9 +450,10 @@
         try {
             assertUriIsUnredacted(img);
 
-            InputStream is = getContentResolver().openInputStream(redactedUri);
-            ExifInterface redactedExifInf = new ExifInterface(is);
-            assertUriIsRedacted(redactedExifInf);
+            try (InputStream is = getContentResolver().openInputStream(redactedUri)) {
+                ExifInterface redactedExifInf = new ExifInterface(is);
+                assertUriIsRedacted(redactedExifInf);
+            }
         } finally {
             img.delete();
         }
@@ -464,10 +466,12 @@
         try {
             assertUriIsUnredacted(img);
 
-            FileDescriptor fd = getContentResolver().openFileDescriptor(redactedUri,
-                    "r").getFileDescriptor();
-            ExifInterface redactedExifInf = new ExifInterface(fd);
-            assertUriIsRedacted(redactedExifInf);
+            try (ParcelFileDescriptor pfd =
+                    getContentResolver().openFileDescriptor(redactedUri, "r")) {
+                FileDescriptor fd = pfd.getFileDescriptor();
+                ExifInterface redactedExifInf = new ExifInterface(fd);
+                assertUriIsRedacted(redactedExifInf);
+            }
         } finally {
             img.delete();
         }
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index 605f85c..ad480e2 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -889,11 +889,11 @@
         try {
             assertThat(file.createNewFile()).isTrue();
 
-            ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
-            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
-
-            assertRWR(readPfd, writePfd);
-            assertUpperFsFd(writePfd); // With cache
+            try (ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+                 ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw")) {
+                assertRWR(readPfd, writePfd);
+                assertUpperFsFd(writePfd); // With cache
+            }
         } finally {
             file.delete();
         }
@@ -907,11 +907,11 @@
         try {
             assertThat(file.createNewFile()).isTrue();
 
-            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
-            ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
-
-            assertRWR(readPfd, writePfd);
-            assertLowerFsFdWithPassthrough(writePfd);
+            try (ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
+                 ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE)) {
+                assertRWR(readPfd, writePfd);
+                assertLowerFsFdWithPassthrough(writePfd);
+            }
         } finally {
             file.delete();
         }
@@ -925,11 +925,11 @@
         try {
             assertThat(file.createNewFile()).isTrue();
 
-            ParcelFileDescriptor writePfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
-            ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
-
-            assertRWR(readPfd, writePfd);
-            assertUpperFsFd(readPfd); // With cache
+            try (ParcelFileDescriptor writePfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+                 ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw")) {
+                assertRWR(readPfd, writePfd);
+                assertUpperFsFd(readPfd); // With cache
+            }
         } finally {
             file.delete();
         }
@@ -943,11 +943,11 @@
         try {
             assertThat(file.createNewFile()).isTrue();
 
-            ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
-            ParcelFileDescriptor writePfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
-
-            assertRWR(readPfd, writePfd);
-            assertLowerFsFdWithPassthrough(readPfd);
+            try (ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
+                 ParcelFileDescriptor writePfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE)) {
+                assertRWR(readPfd, writePfd);
+                assertLowerFsFdWithPassthrough(readPfd);
+            }
         } finally {
             file.delete();
         }
@@ -962,13 +962,13 @@
             assertThat(file.createNewFile()).isTrue();
 
             // We upgrade 'w' only to 'rw'
-            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "w");
-            ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
-
-            assertRWR(readPfd, writePfd);
-            assertRWR(writePfd, readPfd); // Can read on 'w' only pfd
-            assertLowerFsFdWithPassthrough(writePfd);
-            assertLowerFsFdWithPassthrough(readPfd);
+            try (ParcelFileDescriptor writePfd = openWithMediaProvider(file, "w");
+                 ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw")) {
+                assertRWR(readPfd, writePfd);
+                assertRWR(writePfd, readPfd); // Can read on 'w' only pfd
+                assertLowerFsFdWithPassthrough(writePfd);
+                assertLowerFsFdWithPassthrough(readPfd);
+            }
         } finally {
             file.delete();
         }
@@ -985,15 +985,13 @@
 
             // Even if we close the original fd, since we have a dup open
             // the FUSE IO should still bypass the cache
-            try (ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw")) {
-                try (ParcelFileDescriptor writePfdDup = writePfd.dup();
-                     ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(
-                             file, MODE_READ_WRITE)) {
-                    writePfd.close();
+            try (ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
+                 ParcelFileDescriptor writePfdDup = writePfd.dup();
+                 ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE)) {
+                writePfd.close();
 
-                    assertRWR(readPfd, writePfdDup);
-                    assertLowerFsFdWithPassthrough(writePfdDup);
-                }
+                assertRWR(readPfd, writePfdDup);
+                assertLowerFsFdWithPassthrough(writePfdDup);
             }
         } finally {
             file.delete();
@@ -1020,12 +1018,13 @@
             writePfd.close();
 
             // Upper fs open and read without direct_io
-            ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
-            Os.pread(readPfd.getFileDescriptor(), readBuffer, 0, 10, 0);
+            try (ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE)) {
+                Os.pread(readPfd.getFileDescriptor(), readBuffer, 0, 10, 0);
 
-            // Last write on lower fs is visible via upper fs
-            assertThat(readBuffer).isEqualTo(writeBuffer);
-            assertThat(readPfd.getStatSize()).isEqualTo(writeBuffer.length);
+                // Last write on lower fs is visible via upper fs
+                assertThat(readBuffer).isEqualTo(writeBuffer);
+                assertThat(readPfd.getStatSize()).isEqualTo(writeBuffer.length);
+            }
         } finally {
             file.delete();
         }
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index 041e5ce..a04b5d8 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -854,8 +854,7 @@
      * Returns whether we can open the file.
      */
     public static boolean canOpen(File file, boolean forWrite) {
-        try {
-            openWithFilePath(file, forWrite);
+        try (ParcelFileDescriptor ignore = openWithFilePath(file, forWrite)) {
             return true;
         } catch (IOException expected) {
             return false;
@@ -1578,7 +1577,7 @@
     private static boolean isVolumeMounted(String type) {
         try {
             final String volume = executeShellCommand("sm list-volumes " + type).trim();
-            return volume != null && volume.contains("mounted");
+            return volume != null && volume.contains(" mounted");
         } catch (Exception e) {
             return false;
         }
@@ -1592,6 +1591,18 @@
         return isVolumeMounted("emulated");
     }
 
+    private static boolean isFuseReady() {
+        for (String volumeName : MediaStore.getExternalVolumeNames(getContext())) {
+            final Uri uri = MediaStore.Files.getContentUri(volumeName);
+            try (Cursor c = getContentResolver().query(uri, null, null, null)) {
+                assertThat(c).isNotNull();
+            } catch (IllegalArgumentException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Prepare or create a public volume for testing
      */
@@ -1610,6 +1621,8 @@
                     "Timed out while waiting for public volume");
             pollForCondition(TestUtils::isEmulatedVolumeMounted,
                     "Timed out while waiting for emulated volume");
+            pollForCondition(TestUtils::isFuseReady,
+                    "Timed out while waiting for fuse");
         }
     }
 
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index c915235..4f62a91 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -680,7 +680,9 @@
             assertCanQueryAndOpenFile(otherPendingFile, "r");
 
             // We can also read other app's pending file via MediaStore API
-            assertNotNull(openWithMediaProvider(otherPendingFile, "r"));
+            try (ParcelFileDescriptor pfd = openWithMediaProvider(otherPendingFile, "r")) {
+                assertNotNull(pfd);
+            }
         } finally {
             deleteFileAsNoThrow(APP_B_NO_PERMS, otherPendingFile.getAbsolutePath());
         }