Move permission-less tests to device side

The only tests remaining on host side are tests that need the tradefed
contentprovider, need a runtime permission or --no-isolated-storage
instrumentation flag.

BUG: 159593019
Test: atest ScopedStorageDeviceOnlyTest

Change-Id: I611c4753aa8e272ed81614126a524d55d613b4d6
Merged-In: I611c4753aa8e272ed81614126a524d55d613b4d6
(cherry picked from commit 0138863190bbcf9572442132b155155dce02a367)
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index ee0f19a..46ad4a4 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -99,7 +99,7 @@
 }
 
 android_test {
-    name: "ScopedStorageDeviceOnlyTest",
+    name: "CtsScopedStorageDeviceOnlyTest",
     manifest: "device/AndroidManifest.xml",
     test_config: "device/AndroidTest.xml",
     srcs: ["device/**/*.java"],
@@ -108,4 +108,10 @@
     test_suites: ["device-tests", "mts", "cts"],
     sdk_version: "test_current",
     libs: ["android.test.base", "android.test.mock", "android.test.runner",],
+    java_resources: [
+        ":CtsScopedStorageTestAppA",
+        ":CtsScopedStorageTestAppB",
+        ":CtsScopedStorageTestAppC",
+        ":CtsScopedStorageTestAppCLegacy",
+    ]
 }
diff --git a/hostsidetests/scopedstorage/TEST_MAPPING b/hostsidetests/scopedstorage/TEST_MAPPING
index 28ecb56..fd79913 100644
--- a/hostsidetests/scopedstorage/TEST_MAPPING
+++ b/hostsidetests/scopedstorage/TEST_MAPPING
@@ -8,6 +8,9 @@
     },
     {
       "name": "CtsScopedStoragePublicVolumeHostTest"
+    },
+    {
+      "name": "CtsScopedStorageDeviceOnlyTest"
     }
   ]
 }
diff --git a/hostsidetests/scopedstorage/device/AndroidTest.xml b/hostsidetests/scopedstorage/device/AndroidTest.xml
index 9817b8e..e7d17cf 100644
--- a/hostsidetests/scopedstorage/device/AndroidTest.xml
+++ b/hostsidetests/scopedstorage/device/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Runs device-only tests for scoped storage">
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="ScopedStorageDeviceOnlyTest.apk" />
+        <option name="test-file-name" value="CtsScopedStorageDeviceOnlyTest.apk" />
         <option name="install-arg" value="-g" />
     </target_preparer>
 
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 84d80db..faf334b 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -16,34 +16,2689 @@
 
 package android.scopedstorage.cts.device;
 
+import static android.app.AppOpsManager.permissionToOp;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.SystemProperties.getBoolean;
+import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMatch;
+import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMismatch;
+import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata;
+import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromRawResource;
+import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA2;
+import static android.scopedstorage.cts.lib.TestUtils.STR_DATA2;
+import static android.scopedstorage.cts.lib.TestUtils.allowAppOpsToUid;
+import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameDirectory;
+import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameFile;
+import static android.scopedstorage.cts.lib.TestUtils.assertCantRenameDirectory;
+import static android.scopedstorage.cts.lib.TestUtils.assertCantRenameFile;
+import static android.scopedstorage.cts.lib.TestUtils.assertDirectoryContains;
+import static android.scopedstorage.cts.lib.TestUtils.assertFileContent;
+import static android.scopedstorage.cts.lib.TestUtils.assertThrows;
+import static android.scopedstorage.cts.lib.TestUtils.canOpen;
+import static android.scopedstorage.cts.lib.TestUtils.canOpenFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.createFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.deleteFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow;
+import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively;
+import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProvider;
+import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow;
+import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid;
+import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand;
+import static android.scopedstorage.cts.lib.TestUtils.getAlarmsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAndroidDataDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAndroidMediaDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAudiobooksDir;
+import static android.scopedstorage.cts.lib.TestUtils.getContentResolver;
+import static android.scopedstorage.cts.lib.TestUtils.getDcimDir;
+import static android.scopedstorage.cts.lib.TestUtils.getDocumentsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getDownloadDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalFilesDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalMediaDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalStorageDir;
+import static android.scopedstorage.cts.lib.TestUtils.getFileMimeTypeFromDatabase;
+import static android.scopedstorage.cts.lib.TestUtils.getFileOwnerPackageFromDatabase;
+import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase;
+import static android.scopedstorage.cts.lib.TestUtils.getFileSizeFromDatabase;
+import static android.scopedstorage.cts.lib.TestUtils.getFileUri;
+import static android.scopedstorage.cts.lib.TestUtils.getMoviesDir;
+import static android.scopedstorage.cts.lib.TestUtils.getMusicDir;
+import static android.scopedstorage.cts.lib.TestUtils.getNotificationsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getPicturesDir;
+import static android.scopedstorage.cts.lib.TestUtils.getPodcastsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getRingtonesDir;
+import static android.scopedstorage.cts.lib.TestUtils.grantPermission;
+import static android.scopedstorage.cts.lib.TestUtils.installApp;
+import static android.scopedstorage.cts.lib.TestUtils.installAppWithStoragePermissions;
+import static android.scopedstorage.cts.lib.TestUtils.listAs;
+import static android.scopedstorage.cts.lib.TestUtils.openWithMediaProvider;
+import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
+import static android.scopedstorage.cts.lib.TestUtils.queryFile;
+import static android.scopedstorage.cts.lib.TestUtils.queryFileExcludingPending;
+import static android.scopedstorage.cts.lib.TestUtils.queryImageFile;
+import static android.scopedstorage.cts.lib.TestUtils.queryVideoFile;
+import static android.scopedstorage.cts.lib.TestUtils.readExifMetadataFromTestApp;
+import static android.scopedstorage.cts.lib.TestUtils.revokePermission;
+import static android.scopedstorage.cts.lib.TestUtils.setAttrAs;
+import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
+import static android.scopedstorage.cts.lib.TestUtils.uninstallApp;
+import static android.scopedstorage.cts.lib.TestUtils.uninstallAppNoThrow;
+import static android.scopedstorage.cts.lib.TestUtils.updateDisplayNameWithMediaProvider;
+import static android.system.OsConstants.F_OK;
+import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_EXCL;
+import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_TRUNC;
+import static android.system.OsConstants.R_OK;
+import static android.system.OsConstants.S_IRWXU;
+import static android.system.OsConstants.W_OK;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.platform.test.annotations.AppModeInstant;
+import android.provider.MediaStore;
 import android.scopedstorage.cts.lib.TestUtils;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
+import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.cts.install.lib.TestApp;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
 /**
  * Device-side test suite to verify scoped storage business logic.
  */
 @RunWith(AndroidJUnit4.class)
 public class ScopedStorageDeviceTest {
+    public static final String STR_DATA1 = "Just some random text";
+
+    public static final byte[] BYTES_DATA1 = STR_DATA1.getBytes();
+
+    static final String TAG = "ScopedStorageDeviceTest";
+    static final String THIS_PACKAGE_NAME = getContext().getPackageName();
+
+    /**
+     * To help avoid flaky tests, give ourselves a unique nonce to be used for
+     * all filesystem paths, so that we don't risk conflicting with previous
+     * test runs.
+     */
+    static final String NONCE = String.valueOf(System.nanoTime());
+
+    static final String TEST_DIRECTORY_NAME = "ScopedStorageDeviceTestDirectory" + NONCE;
+
+    static final String AUDIO_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".mp3";
+    static final String PLAYLIST_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".m3u";
+    static final String SUBTITLE_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".srt";
+    static final String VIDEO_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".mp4";
+    static final String IMAGE_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".jpg";
+    static final String NONMEDIA_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".pdf";
+
+    static final String FILE_CREATION_ERROR_MESSAGE = "No such file or directory";
+
+    private static final TestApp TEST_APP_A = new TestApp("TestAppA",
+            "android.scopedstorage.cts.testapp.A", 1, false, "CtsScopedStorageTestAppA.apk");
+    private static final TestApp TEST_APP_B = new TestApp("TestAppB",
+            "android.scopedstorage.cts.testapp.B", 1, false, "CtsScopedStorageTestAppB.apk");
+    private static final TestApp TEST_APP_C = new TestApp("TestAppC",
+            "android.scopedstorage.cts.testapp.C", 1, false, "CtsScopedStorageTestAppC.apk");
+    private static final TestApp TEST_APP_C_LEGACY = new TestApp("TestAppCLegacy",
+            "android.scopedstorage.cts.testapp.C", 1, false, "CtsScopedStorageTestAppCLegacy.apk");
+    private static final String[] SYSTEM_GALERY_APPOPS = {
+            AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES, AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO};
+    private static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
+            permissionToOp(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
 
     @BeforeClass
-    public static void deletePublicVolumes() throws Exception {
+    public static void verifyTestsWillRunOnPrimaryVolume() throws Exception {
         TestUtils.resetDefaultExternalStorageVolume();
+        TestUtils.assertDefaultVolumeIsPrimary();
+    }
+
+    @BeforeClass
+    public static void createPublicVolume() throws Exception {
+        // Create a public volume. It's not used in this test right now, but it makes for a less
+        // flaky test to create here. ScopedStoragePublicVolumeDeviceTest will be folded into
+        // this test later, as a prametererized option, as tracked in b/159593019.
+        if (TestUtils.getCurrentPublicVolumeName() == null) {
+            TestUtils.createNewPublicVolume();
+        }
+    }
+
+    @Before
+    public void setup() throws Exception {
+        // skips all test cases if FUSE is not active.
+        assumeTrue(getBoolean("persist.sys.fuse", false));
+
+        if (!getContext().getPackageManager().isInstantApp()) {
+            pollForExternalStorageState();
+            getExternalFilesDir().mkdirs();
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        executeShellCommand("rm -r /sdcard/Android/data/com.android.shell");
+    }
+
+    @Before
+    public void setupExternalStorage() {
+        setupDefaultDirectories();
     }
 
     /**
-     * No-op test.
+     * Test that we enforce certain media types can only be created in certain directories.
      */
     @Test
-    public void noopTest() {
-        // TODO(159593019): Move tests that don't require host side support from
-        // ScopedStorageTest here.
-        assertThat(5).isAtLeast(4);
+    public void testTypePathConformity() throws Exception {
+        final File dcimDir = getDcimDir();
+        final File documentsDir = getDocumentsDir();
+        final File downloadDir = getDownloadDir();
+        final File moviesDir = getMoviesDir();
+        final File musicDir = getMusicDir();
+        final File picturesDir = getPicturesDir();
+        // Only audio files can be created in Music
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(musicDir, NONMEDIA_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(musicDir, VIDEO_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(musicDir, IMAGE_FILE_NAME).createNewFile();
+                });
+        // Only video files can be created in Movies
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(moviesDir, NONMEDIA_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(moviesDir, AUDIO_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(moviesDir, IMAGE_FILE_NAME).createNewFile();
+                });
+        // Only image and video files can be created in DCIM
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(dcimDir, NONMEDIA_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(dcimDir, AUDIO_FILE_NAME).createNewFile();
+                });
+        // Only image and video files can be created in Pictures
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(picturesDir, NONMEDIA_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(picturesDir, AUDIO_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(picturesDir, PLAYLIST_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(dcimDir, SUBTITLE_FILE_NAME).createNewFile();
+                });
+
+        assertCanCreateFile(new File(getAlarmsDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(getAudiobooksDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(dcimDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(dcimDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, NONMEDIA_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, PLAYLIST_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, SUBTITLE_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, NONMEDIA_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, PLAYLIST_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, SUBTITLE_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(moviesDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(moviesDir, SUBTITLE_FILE_NAME));
+        assertCanCreateFile(new File(musicDir, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(musicDir, PLAYLIST_FILE_NAME));
+        assertCanCreateFile(new File(getNotificationsDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(picturesDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(picturesDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(getPodcastsDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(getRingtonesDir(), AUDIO_FILE_NAME));
+
+        // No file whatsoever can be created in the top level directory
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(getExternalStorageDir(), NONMEDIA_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(getExternalStorageDir(), AUDIO_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(getExternalStorageDir(), IMAGE_FILE_NAME).createNewFile();
+                });
+        assertThrows(IOException.class, "Operation not permitted",
+                () -> {
+                    new File(getExternalStorageDir(), VIDEO_FILE_NAME).createNewFile();
+                });
+    }
+
+    /**
+     * Test that we can create a file in app's external files directory,
+     * and that we can write and read to/from the file.
+     */
+    @Test
+    public void testCreateFileInAppExternalDir() throws Exception {
+        final File file = new File(getExternalFilesDir(), "text.txt");
+        try {
+            assertThat(file.createNewFile()).isTrue();
+            assertThat(file.delete()).isTrue();
+            // Ensure the file is properly deleted and can be created again
+            assertThat(file.createNewFile()).isTrue();
+
+            // Write to file
+            try (FileOutputStream fos = new FileOutputStream(file)) {
+                fos.write(BYTES_DATA1);
+            }
+
+            // Read the same data from file
+            assertFileContent(file, BYTES_DATA1);
+        } finally {
+            file.delete();
+        }
+    }
+
+    /**
+     * Test that we can't create a file in another app's external files directory,
+     * and that we'll get the same error regardless of whether the app exists or not.
+     */
+    @Test
+    public void testCreateFileInOtherAppExternalDir() throws Exception {
+        // Creating a file in a non existent package dir should return ENOENT, as expected
+        final File nonexistentPackageFileDir = new File(
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
+        final File file1 = new File(nonexistentPackageFileDir, NONMEDIA_FILE_NAME);
+        assertThrows(
+                IOException.class, FILE_CREATION_ERROR_MESSAGE, () -> {
+                    file1.createNewFile();
+                });
+
+        // Creating a file in an existent package dir should give the same error string to avoid
+        // leaking installed app names, and we know the following directory exists because shell
+        // mkdirs it in test setup
+        final File shellPackageFileDir = new File(
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
+        final File file2 = new File(shellPackageFileDir, NONMEDIA_FILE_NAME);
+        assertThrows(
+                IOException.class, FILE_CREATION_ERROR_MESSAGE, () -> {
+                    file1.createNewFile();
+                });
+    }
+
+    /**
+     * Test that apps can't read/write files in another app's external files directory,
+     * and can do so in their own app's external file directory.
+     */
+    @Test
+    public void testReadWriteFilesInOtherAppExternalDir() throws Exception {
+        final File videoFile = new File(getExternalFilesDir(), VIDEO_FILE_NAME);
+
+        try {
+            // Create a file in app's external files directory
+            if (!videoFile.exists()) {
+                assertThat(videoFile.createNewFile()).isTrue();
+            }
+
+            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            // TEST_APP_A should not be able to read/write to other app's external files directory.
+            assertThat(canOpenFileAs(TEST_APP_A, videoFile, false /* forWrite */)).isFalse();
+            assertThat(canOpenFileAs(TEST_APP_A, videoFile, true /* forWrite */)).isFalse();
+            // TEST_APP_A should not be able to delete files in other app's external files
+            // directory.
+            assertThat(deleteFileAs(TEST_APP_A, videoFile.getPath())).isFalse();
+
+            // Apps should have read/write access in their own app's external files directory.
+            assertThat(canOpen(videoFile, false /* forWrite */)).isTrue();
+            assertThat(canOpen(videoFile, true /* forWrite */)).isTrue();
+            // Apps should be able to delete files in their own app's external files directory.
+            assertThat(videoFile.delete()).isTrue();
+        } finally {
+            videoFile.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that we can contribute media without any permissions.
+     */
+    @Test
+    public void testContributeMediaFile() throws Exception {
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+
+        try {
+            assertThat(imageFile.createNewFile()).isTrue();
+
+            // Ensure that the file was successfully added to the MediaProvider database
+            assertThat(getFileOwnerPackageFromDatabase(imageFile)).isEqualTo(THIS_PACKAGE_NAME);
+
+            // Try to write random data to the file
+            try (FileOutputStream fos = new FileOutputStream(imageFile)) {
+                fos.write(BYTES_DATA1);
+                fos.write(BYTES_DATA2);
+            }
+
+            final byte[] expected = (STR_DATA1 + STR_DATA2).getBytes();
+            assertFileContent(imageFile, expected);
+
+            // Closing the file after writing will not trigger a MediaScan. Call scanFile to update
+            // file's entry in MediaProvider's database.
+            assertThat(MediaStore.scanFile(getContentResolver(), imageFile)).isNotNull();
+
+            // Ensure that the scan was completed and the file's size was updated.
+            assertThat(getFileSizeFromDatabase(imageFile)).isEqualTo(
+                    BYTES_DATA1.length + BYTES_DATA2.length);
+        } finally {
+            imageFile.delete();
+        }
+        // Ensure that delete makes a call to MediaProvider to remove the file from its database.
+        assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(-1);
+    }
+
+    @Test
+    public void testCreateAndDeleteEmptyDir() throws Exception {
+        final File externalFilesDir = getExternalFilesDir();
+        // Remove directory in order to create it again
+        externalFilesDir.delete();
+
+        // Can create own external files dir
+        assertThat(externalFilesDir.mkdir()).isTrue();
+
+        final File dir1 = new File(externalFilesDir, "random_dir");
+        // Can create dirs inside it
+        assertThat(dir1.mkdir()).isTrue();
+
+        final File dir2 = new File(dir1, "random_dir_inside_random_dir");
+        // And create a dir inside the new dir
+        assertThat(dir2.mkdir()).isTrue();
+
+        // And can delete them all
+        assertThat(dir2.delete()).isTrue();
+        assertThat(dir1.delete()).isTrue();
+        assertThat(externalFilesDir.delete()).isTrue();
+
+        // Can't create external dir for other apps
+        final File nonexistentPackageFileDir = new File(
+                externalFilesDir.getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
+        final File shellPackageFileDir = new File(
+                externalFilesDir.getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
+
+        assertThat(nonexistentPackageFileDir.mkdir()).isFalse();
+        assertThat(shellPackageFileDir.mkdir()).isFalse();
+    }
+
+    @Test
+    public void testCantAccessOtherAppsContents() throws Exception {
+        final File mediaFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        try {
+            installApp(TEST_APP_A);
+
+            assertThat(createFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
+            assertThat(createFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
+
+            // We can still see that the files exist
+            assertThat(mediaFile.exists()).isTrue();
+            assertThat(nonMediaFile.exists()).isTrue();
+
+            // But we can't access their content
+            assertThat(canOpen(mediaFile, /* forWrite */ false)).isFalse();
+            assertThat(canOpen(nonMediaFile, /* forWrite */ true)).isFalse();
+            assertThat(canOpen(mediaFile, /* forWrite */ false)).isFalse();
+            assertThat(canOpen(nonMediaFile, /* forWrite */ true)).isFalse();
+        } finally {
+            deleteFileAsNoThrow(TEST_APP_A, nonMediaFile.getPath());
+            deleteFileAsNoThrow(TEST_APP_A, mediaFile.getPath());
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testCantDeleteOtherAppsContents() throws Exception {
+        final File dirInDownload = new File(getDownloadDir(), TEST_DIRECTORY_NAME);
+        final File mediaFile = new File(dirInDownload, IMAGE_FILE_NAME);
+        final File nonMediaFile = new File(dirInDownload, NONMEDIA_FILE_NAME);
+        try {
+            installApp(TEST_APP_A);
+            assertThat(dirInDownload.mkdir()).isTrue();
+            // Have another app create a media file in the directory
+            assertThat(createFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
+
+            // Can't delete the directory since it contains another app's content
+            assertThat(dirInDownload.delete()).isFalse();
+            // Can't delete another app's content
+            assertThat(deleteRecursively(dirInDownload)).isFalse();
+
+            // Have another app create a non-media file in the directory
+            assertThat(createFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
+
+            // Can't delete the directory since it contains another app's content
+            assertThat(dirInDownload.delete()).isFalse();
+            // Can't delete another app's content
+            assertThat(deleteRecursively(dirInDownload)).isFalse();
+
+            // Delete only the media file and keep the non-media file
+            assertThat(deleteFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
+            // Directory now has only the non-media file contributed by another app, so we still
+            // can't delete it nor its content
+            assertThat(dirInDownload.delete()).isFalse();
+            assertThat(deleteRecursively(dirInDownload)).isFalse();
+
+            // Delete the last file belonging to another app
+            assertThat(deleteFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
+            // Create our own file
+            assertThat(nonMediaFile.createNewFile()).isTrue();
+
+            // Now that the directory only has content that was contributed by us, we can delete it
+            assertThat(deleteRecursively(dirInDownload)).isTrue();
+        } finally {
+            deleteFileAsNoThrow(TEST_APP_A, nonMediaFile.getPath());
+            deleteFileAsNoThrow(TEST_APP_A, mediaFile.getPath());
+            // At this point, we're not sure who created this file, so we'll have both apps
+            // deleting it
+            mediaFile.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+            dirInDownload.delete();
+        }
+    }
+
+    /**
+     * Test that deleting uri corresponding to a file which was already deleted via filePath
+     * doesn't result in a security exception.
+     */
+    @Test
+    public void testDeleteAlreadyUnlinkedFile() throws Exception {
+        final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        try {
+            assertTrue(nonMediaFile.createNewFile());
+            final Uri uri = MediaStore.scanFile(getContentResolver(), nonMediaFile);
+            assertNotNull(uri);
+
+            // Delete the file via filePath
+            assertTrue(nonMediaFile.delete());
+
+            // If we delete nonMediaFile with ContentResolver#delete, it shouldn't result in a
+            // security exception.
+            assertThat(getContentResolver().delete(uri, Bundle.EMPTY)).isEqualTo(0);
+        } finally {
+            nonMediaFile.delete();
+        }
+    }
+
+    /**
+     * This test relies on the fact that {@link File#list} uses opendir internally, and that it
+     * returns {@code null} if opendir fails.
+     */
+    @Test
+    public void testOpendirRestrictions() throws Exception {
+        // Opening a non existent package directory should fail, as expected
+        final File nonexistentPackageFileDir = new File(
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
+        assertThat(nonexistentPackageFileDir.list()).isNull();
+
+        // Opening another package's external directory should fail as well, even if it exists
+        final File shellPackageFileDir = new File(
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
+        assertThat(shellPackageFileDir.list()).isNull();
+
+        // We can open our own external files directory
+        final String[] filesList = getExternalFilesDir().list();
+        assertThat(filesList).isNotNull();
+
+        // We can open any public directory in external storage
+        assertThat(getDcimDir().list()).isNotNull();
+        assertThat(getDownloadDir().list()).isNotNull();
+        assertThat(getMoviesDir().list()).isNotNull();
+        assertThat(getMusicDir().list()).isNotNull();
+
+        // We can open the root directory of external storage
+        final String[] topLevelDirs = getExternalStorageDir().list();
+        assertThat(topLevelDirs).isNotNull();
+        // TODO(b/145287327): This check fails on a device with no visible files.
+        // This can be fixed if we display default directories.
+        // assertThat(topLevelDirs).isNotEmpty();
+    }
+
+    @Test
+    public void testLowLevelFileIO() throws Exception {
+        String filePath = new File(getDownloadDir(), NONMEDIA_FILE_NAME).toString();
+        try {
+            int createFlags = O_CREAT | O_RDWR;
+            int createExclFlags = createFlags | O_EXCL;
+
+            FileDescriptor fd = Os.open(filePath, createExclFlags, S_IRWXU);
+            Os.close(fd);
+            assertThrows(
+                    ErrnoException.class, () -> {
+                        Os.open(filePath, createExclFlags, S_IRWXU);
+                    });
+
+            fd = Os.open(filePath, createFlags, S_IRWXU);
+            try {
+                assertThat(Os.write(fd,
+                        ByteBuffer.wrap(BYTES_DATA1))).isEqualTo(BYTES_DATA1.length);
+                assertFileContent(fd, BYTES_DATA1);
+            } finally {
+                Os.close(fd);
+            }
+            // should just append the data
+            fd = Os.open(filePath, createFlags | O_APPEND, S_IRWXU);
+            try {
+                assertThat(Os.write(fd,
+                        ByteBuffer.wrap(BYTES_DATA2))).isEqualTo(BYTES_DATA2.length);
+                final byte[] expected = (STR_DATA1 + STR_DATA2).getBytes();
+                assertFileContent(fd, expected);
+            } finally {
+                Os.close(fd);
+            }
+            // should overwrite everything
+            fd = Os.open(filePath, createFlags | O_TRUNC, S_IRWXU);
+            try {
+                final byte[] otherData = "this is different data".getBytes();
+                assertThat(Os.write(fd, ByteBuffer.wrap(otherData))).isEqualTo(otherData.length);
+                assertFileContent(fd, otherData);
+            } finally {
+                Os.close(fd);
+            }
+        } finally {
+            new File(filePath).delete();
+        }
+    }
+
+    /**
+     * Test that media files from other packages are only visible to apps with storage permission.
+     */
+    @Test
+    public void testListDirectoriesWithMediaFiles() throws Exception {
+        final File dcimDir = getDcimDir();
+        final File dir = new File(dcimDir, TEST_DIRECTORY_NAME);
+        final File videoFile = new File(dir, VIDEO_FILE_NAME);
+        final String videoFileName = videoFile.getName();
+        try {
+            if (!dir.exists()) {
+                assertThat(dir.mkdir()).isTrue();
+            }
+
+            // Install TEST_APP_A and create media file in the new directory.
+            installApp(TEST_APP_A);
+            assertThat(createFileAs(TEST_APP_A, videoFile.getPath())).isTrue();
+            // TEST_APP_A should see TEST_DIRECTORY in DCIM and new file in TEST_DIRECTORY.
+            assertThat(listAs(TEST_APP_A, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(videoFileName);
+
+            // Install TEST_APP_B with storage permission.
+            installAppWithStoragePermissions(TEST_APP_B);
+            // TEST_APP_B with storage permission should see TEST_DIRECTORY in DCIM and new file
+            // in TEST_DIRECTORY.
+            assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_B, dir.getPath())).containsExactly(videoFileName);
+
+            // Revoke storage permission for TEST_APP_B
+            revokePermission(
+                    TEST_APP_B.getPackageName(), Manifest.permission.READ_EXTERNAL_STORAGE);
+            // TEST_APP_B without storage permission should see TEST_DIRECTORY in DCIM and should
+            // not see new file in new TEST_DIRECTORY.
+            assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(videoFileName);
+        } finally {
+            uninstallAppNoThrow(TEST_APP_B);
+            deleteFileAsNoThrow(TEST_APP_A, videoFile.getPath());
+            dir.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that app can't see non-media files created by other packages
+     */
+    @Test
+    public void testListDirectoriesWithNonMediaFiles() throws Exception {
+        final File downloadDir = getDownloadDir();
+        final File dir = new File(downloadDir, TEST_DIRECTORY_NAME);
+        final File pdfFile = new File(dir, NONMEDIA_FILE_NAME);
+        final String pdfFileName = pdfFile.getName();
+        try {
+            if (!dir.exists()) {
+                assertThat(dir.mkdir()).isTrue();
+            }
+
+            // Install TEST_APP_A and create non media file in the new directory.
+            installApp(TEST_APP_A);
+            assertThat(createFileAs(TEST_APP_A, pdfFile.getPath())).isTrue();
+
+            // TEST_APP_A should see TEST_DIRECTORY in downloadDir and new non media file in
+            // TEST_DIRECTORY.
+            assertThat(listAs(TEST_APP_A, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(pdfFileName);
+
+            // Install TEST_APP_B with storage permission.
+            installAppWithStoragePermissions(TEST_APP_B);
+            // TEST_APP_B with storage permission should see TEST_DIRECTORY in downloadDir
+            // and should not see new non media file in TEST_DIRECTORY.
+            assertThat(listAs(TEST_APP_B, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(pdfFileName);
+        } finally {
+            uninstallAppNoThrow(TEST_APP_B);
+            deleteFileAsNoThrow(TEST_APP_A, pdfFile.getPath());
+            dir.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that app can only see its directory in Android/data.
+     */
+    @Test
+    public void testListFilesFromExternalFilesDirectory() throws Exception {
+        final String packageName = THIS_PACKAGE_NAME;
+        final File nonmediaFile = new File(getExternalFilesDir(), NONMEDIA_FILE_NAME);
+
+        try {
+            // Create a file in app's external files directory
+            if (!nonmediaFile.exists()) {
+                assertThat(nonmediaFile.createNewFile()).isTrue();
+            }
+            // App should see its directory and directories of shared packages. App should see all
+            // files and directories in its external directory.
+            assertDirectoryContains(nonmediaFile.getParentFile(), nonmediaFile);
+
+            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
+            // TEST_APP_A should not see other app's external files directory.
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            assertThrows(IOException.class,
+                    () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
+            assertThrows(IOException.class,
+                    () -> listAs(TEST_APP_A, getExternalFilesDir().getPath()));
+        } finally {
+            nonmediaFile.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that app can see files and directories in Android/media.
+     */
+    @Test
+    public void testListFilesFromExternalMediaDirectory() throws Exception {
+        final File videoFile = new File(getExternalMediaDir(), VIDEO_FILE_NAME);
+
+        try {
+            // Create a file in app's external media directory
+            if (!videoFile.exists()) {
+                assertThat(videoFile.createNewFile()).isTrue();
+            }
+
+            // App should see its directory and other app's external media directories with media
+            // files.
+            assertDirectoryContains(videoFile.getParentFile(), videoFile);
+
+            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
+            // TEST_APP_A with storage permission should see other app's external media directory.
+            installAppWithStoragePermissions(TEST_APP_A);
+            // Apps with READ_EXTERNAL_STORAGE can list files in other app's external media
+            // directory.
+            assertThat(listAs(TEST_APP_A, getAndroidMediaDir().getPath()))
+                    .contains(THIS_PACKAGE_NAME);
+            assertThat(listAs(TEST_APP_A, getExternalMediaDir().getPath()))
+                    .containsExactly(videoFile.getName());
+        } finally {
+            videoFile.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testMetaDataRedaction() throws Exception {
+        File jpgFile = new File(getPicturesDir(), "img_metadata.jpg");
+        try {
+            if (jpgFile.exists()) {
+                assertThat(jpgFile.delete()).isTrue();
+            }
+
+            HashMap<String, String> originalExif =
+                    getExifMetadataFromRawResource(R.raw.img_with_metadata);
+
+            try (InputStream in =
+                         getContext().getResources().openRawResource(R.raw.img_with_metadata);
+                 OutputStream out = new FileOutputStream(jpgFile)) {
+                // Dump the image we have to external storage
+                FileUtils.copy(in, out);
+
+                HashMap<String, String> exif = getExifMetadata(jpgFile);
+                assertExifMetadataMatch(exif, originalExif);
+
+                installAppWithStoragePermissions(TEST_APP_A);
+                HashMap<String, String> exifFromTestApp =
+                        readExifMetadataFromTestApp(TEST_APP_A, jpgFile.getPath());
+                // Other apps shouldn't have access to the same metadata without explicit permission
+                assertExifMetadataMismatch(exifFromTestApp, originalExif);
+
+                // TODO(b/146346138): Test that if we give TEST_APP_A write URI permission,
+                //  it would be able to access the metadata.
+            } // Intentionally keep the original streams open during the test so bytes are more
+            // likely to be in the VFS cache from both file opens
+        } finally {
+            jpgFile.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testOpenFilePathFirstWriteContentResolver() throws Exception {
+        String displayName = "open_file_path_write_content_resolver.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        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
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testOpenContentResolverFirstWriteContentResolver() throws Exception {
+        String displayName = "open_content_resolver_write_content_resolver.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        try {
+            assertThat(file.createNewFile()).isTrue();
+
+            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
+            ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+
+            assertRWR(readPfd, writePfd);
+            assertLowerFsFd(writePfd);
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testOpenFilePathFirstWriteFilePath() throws Exception {
+        String displayName = "open_file_path_write_file_path.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        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
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testOpenContentResolverFirstWriteFilePath() throws Exception {
+        String displayName = "open_content_resolver_write_file_path.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        try {
+            assertThat(file.createNewFile()).isTrue();
+
+            ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
+            ParcelFileDescriptor writePfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+
+            assertRWR(readPfd, writePfd);
+            assertLowerFsFd(readPfd);
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testOpenContentResolverWriteOnly() throws Exception {
+        String displayName = "open_content_resolver_write_only.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        try {
+            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
+            assertLowerFsFd(writePfd);
+            assertLowerFsFd(readPfd);
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testOpenContentResolverDup() throws Exception {
+        String displayName = "open_content_resolver_dup.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        try {
+            file.delete();
+            assertThat(file.createNewFile()).isTrue();
+
+            // 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();
+
+                    assertRWR(readPfd, writePfdDup);
+                    assertLowerFsFd(writePfdDup);
+                }
+            }
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testOpenContentResolverClose() throws Exception {
+        String displayName = "open_content_resolver_close.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        try {
+            byte[] readBuffer = new byte[10];
+            byte[] writeBuffer = new byte[10];
+            Arrays.fill(writeBuffer, (byte) 1);
+
+            assertThat(file.createNewFile()).isTrue();
+
+            // Lower fs open and write
+            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
+            Os.pwrite(writePfd.getFileDescriptor(), writeBuffer, 0, 10, 0);
+
+            // Close so upper fs open will not use direct_io
+            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);
+
+            // Last write on lower fs is visible via upper fs
+            assertThat(readBuffer).isEqualTo(writeBuffer);
+            assertThat(readPfd.getStatSize()).isEqualTo(writeBuffer.length);
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testContentResolverDelete() throws Exception {
+        String displayName = "content_resolver_delete.jpg";
+        File file = new File(getDcimDir(), displayName);
+
+        try {
+            assertThat(file.createNewFile()).isTrue();
+
+            deleteWithMediaProvider(file);
+
+            assertThat(file.exists()).isFalse();
+            assertThat(file.createNewFile()).isTrue();
+        } finally {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testContentResolverUpdate() throws Exception {
+        String oldDisplayName = "content_resolver_update_old.jpg";
+        String newDisplayName = "content_resolver_update_new.jpg";
+        File oldFile = new File(getDcimDir(), oldDisplayName);
+        File newFile = new File(getDcimDir(), newDisplayName);
+
+        try {
+            assertThat(oldFile.createNewFile()).isTrue();
+            // Publish the pending oldFile before updating with MediaProvider. Not publishing the
+            // file will make MP consider pending from FUSE as explicit IS_PENDING
+            final Uri uri = MediaStore.scanFile(getContentResolver(), oldFile);
+            assertNotNull(uri);
+
+            updateDisplayNameWithMediaProvider(uri,
+                    Environment.DIRECTORY_DCIM, oldDisplayName, newDisplayName);
+
+            assertThat(oldFile.exists()).isFalse();
+            assertThat(oldFile.createNewFile()).isTrue();
+            assertThat(newFile.exists()).isTrue();
+            assertThat(newFile.createNewFile()).isFalse();
+        } finally {
+            oldFile.delete();
+            newFile.delete();
+        }
+    }
+
+    @Test
+    public void testCreateLowerCaseDeleteUpperCase() throws Exception {
+        File upperCase = new File(getDownloadDir(), "CREATE_LOWER_DELETE_UPPER");
+        File lowerCase = new File(getDownloadDir(), "create_lower_delete_upper");
+
+        createDeleteCreate(lowerCase, upperCase);
+    }
+
+    @Test
+    public void testCreateUpperCaseDeleteLowerCase() throws Exception {
+        File upperCase = new File(getDownloadDir(), "CREATE_UPPER_DELETE_LOWER");
+        File lowerCase = new File(getDownloadDir(), "create_upper_delete_lower");
+
+        createDeleteCreate(upperCase, lowerCase);
+    }
+
+    @Test
+    public void testCreateMixedCaseDeleteDifferentMixedCase() throws Exception {
+        File mixedCase1 = new File(getDownloadDir(), "CrEaTe_MiXeD_dElEtE_mIxEd");
+        File mixedCase2 = new File(getDownloadDir(), "cReAtE_mIxEd_DeLeTe_MiXeD");
+
+        createDeleteCreate(mixedCase1, mixedCase2);
+    }
+
+    @Test
+    public void testAndroidDataObbDoesNotForgetMount() throws Exception {
+        File dataDir = getContext().getExternalFilesDir(null);
+        File upperCaseDataDir = new File(dataDir.getPath().replace("Android/data", "ANDROID/DATA"));
+
+        File obbDir = getContext().getObbDir();
+        File upperCaseObbDir = new File(obbDir.getPath().replace("Android/obb", "ANDROID/OBB"));
+
+
+        StructStat beforeDataStruct = Os.stat(dataDir.getPath());
+        StructStat beforeObbStruct = Os.stat(obbDir.getPath());
+
+        assertThat(dataDir.exists()).isTrue();
+        assertThat(upperCaseDataDir.exists()).isTrue();
+        assertThat(obbDir.exists()).isTrue();
+        assertThat(upperCaseObbDir.exists()).isTrue();
+
+        StructStat afterDataStruct = Os.stat(upperCaseDataDir.getPath());
+        StructStat afterObbStruct = Os.stat(upperCaseObbDir.getPath());
+
+        assertThat(beforeDataStruct.st_dev).isEqualTo(afterDataStruct.st_dev);
+        assertThat(beforeObbStruct.st_dev).isEqualTo(afterObbStruct.st_dev);
+    }
+
+    @Test
+    public void testCacheConsistencyForCaseInsensitivity() throws Exception {
+        File upperCaseFile = new File(getDownloadDir(), "CACHE_CONSISTENCY_FOR_CASE_INSENSITIVITY");
+        File lowerCaseFile = new File(getDownloadDir(), "cache_consistency_for_case_insensitivity");
+
+        try {
+            ParcelFileDescriptor upperCasePfd =
+                    ParcelFileDescriptor.open(upperCaseFile, MODE_READ_WRITE | MODE_CREATE);
+            ParcelFileDescriptor lowerCasePfd =
+                    ParcelFileDescriptor.open(lowerCaseFile, MODE_READ_WRITE | MODE_CREATE);
+
+            assertRWR(upperCasePfd, lowerCasePfd);
+            assertRWR(lowerCasePfd, upperCasePfd);
+        } finally {
+            upperCaseFile.delete();
+            lowerCaseFile.delete();
+        }
+    }
+
+    private void createDeleteCreate(File create, File delete) throws Exception {
+        try {
+            assertThat(create.createNewFile()).isTrue();
+            Thread.sleep(5);
+
+            assertThat(delete.delete()).isTrue();
+            Thread.sleep(5);
+
+            assertThat(create.createNewFile()).isTrue();
+            Thread.sleep(5);
+        } finally {
+            create.delete();
+            delete.delete();
+        }
+    }
+
+    @Test
+    public void testReadStorageInvalidation() throws Exception {
+        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "read_storage.jpg"),
+                Manifest.permission.READ_EXTERNAL_STORAGE,
+                AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, /* forWrite */ false);
+    }
+
+    @Test
+    public void testWriteStorageInvalidation() throws Exception {
+        testAppOpInvalidation(TEST_APP_C_LEGACY, new File(getDcimDir(), "write_storage.jpg"),
+                Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE, /* forWrite */ true);
+    }
+
+    @Test
+    public void testManageStorageInvalidation() throws Exception {
+        testAppOpInvalidation(TEST_APP_C, new File(getDownloadDir(), "manage_storage.pdf"),
+                /* permission */ null, OPSTR_MANAGE_EXTERNAL_STORAGE, /* forWrite */ true);
+    }
+
+    @Test
+    public void testWriteImagesInvalidation() throws Exception {
+        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_images.jpg"),
+                /* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES, /* forWrite */ true);
+    }
+
+    @Test
+    public void testWriteVideoInvalidation() throws Exception {
+        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_video.mp4"),
+                /* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO, /* forWrite */ true);
+    }
+
+    @Test
+    public void testAccessMediaLocationInvalidation() throws Exception {
+        File imgFile = new File(getDcimDir(), "access_media_location.jpg");
+
+        try {
+            // Setup image with sensitive data on external storage
+            HashMap<String, String> originalExif =
+                    getExifMetadataFromRawResource(R.raw.img_with_metadata);
+            try (InputStream in =
+                         getContext().getResources().openRawResource(R.raw.img_with_metadata);
+                 OutputStream out = new FileOutputStream(imgFile)) {
+                // Dump the image we have to external storage
+                FileUtils.copy(in, out);
+            }
+            HashMap<String, String> exif = getExifMetadata(imgFile);
+            assertExifMetadataMatch(exif, originalExif);
+
+            // Install test app
+            installAppWithStoragePermissions(TEST_APP_C);
+
+            // Grant A_M_L and verify access to sensitive data
+            grantPermission(TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
+            HashMap<String, String> exifFromTestApp =
+                    readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
+            assertExifMetadataMatch(exifFromTestApp, originalExif);
+
+            // Revoke A_M_L and verify sensitive data redaction
+            revokePermission(
+                    TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
+            exifFromTestApp = readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
+            assertExifMetadataMismatch(exifFromTestApp, originalExif);
+
+            // Re-grant A_M_L and verify access to sensitive data
+            grantPermission(TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
+            exifFromTestApp = readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
+            assertExifMetadataMatch(exifFromTestApp, originalExif);
+        } finally {
+            imgFile.delete();
+            uninstallAppNoThrow(TEST_APP_C);
+        }
+    }
+
+    @Test
+    public void testAppUpdateInvalidation() throws Exception {
+        File file = new File(getDcimDir(), "app_update.jpg");
+        try {
+            assertThat(file.createNewFile()).isTrue();
+
+            // Install legacy
+            installAppWithStoragePermissions(TEST_APP_C_LEGACY);
+            grantPermission(TEST_APP_C_LEGACY.getPackageName(),
+                    Manifest.permission.WRITE_EXTERNAL_STORAGE); // Grants write access for legacy
+            // Legacy app can read and write media files contributed by others
+            assertThat(canOpenFileAs(TEST_APP_C_LEGACY, file, /* forWrite */ false)).isTrue();
+            assertThat(canOpenFileAs(TEST_APP_C_LEGACY, file, /* forWrite */ true)).isTrue();
+
+            // Update to non-legacy
+            installAppWithStoragePermissions(TEST_APP_C);
+            grantPermission(TEST_APP_C_LEGACY.getPackageName(),
+                    Manifest.permission.WRITE_EXTERNAL_STORAGE); // No effect for non-legacy
+            // Non-legacy app can read media files contributed by others
+            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isTrue();
+            // But cannot write
+            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ true)).isFalse();
+        } finally {
+            file.delete();
+            uninstallAppNoThrow(TEST_APP_C);
+        }
+    }
+
+    @Test
+    public void testAppReinstallInvalidation() throws Exception {
+        File file = new File(getDcimDir(), "app_reinstall.jpg");
+
+        try {
+            assertThat(file.createNewFile()).isTrue();
+
+            // Install
+            installAppWithStoragePermissions(TEST_APP_C);
+            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isTrue();
+
+            // Re-install
+            uninstallAppNoThrow(TEST_APP_C);
+            installApp(TEST_APP_C);
+            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isFalse();
+        } finally {
+            file.delete();
+            uninstallAppNoThrow(TEST_APP_C);
+        }
+    }
+
+    private void testAppOpInvalidation(TestApp app, File file, @Nullable String permission,
+            String opstr, boolean forWrite) throws Exception {
+        try {
+            installApp(app);
+            assertThat(file.createNewFile()).isTrue();
+            assertAppOpInvalidation(app, file, permission, opstr, forWrite);
+        } finally {
+            file.delete();
+            uninstallApp(app);
+        }
+    }
+
+    /** If {@code permission} is null, appops are flipped, otherwise permissions are flipped */
+    private void assertAppOpInvalidation(TestApp app, File file, @Nullable String permission,
+            String opstr, boolean forWrite) throws Exception {
+        String packageName = app.getPackageName();
+        int uid = getContext().getPackageManager().getPackageUid(packageName, 0);
+
+        // Deny
+        if (permission != null) {
+            revokePermission(packageName, permission);
+        } else {
+            denyAppOpsToUid(uid, opstr);
+        }
+        assertThat(canOpenFileAs(app, file, forWrite)).isFalse();
+
+        // Grant
+        if (permission != null) {
+            grantPermission(packageName, permission);
+        } else {
+            allowAppOpsToUid(uid, opstr);
+        }
+        assertThat(canOpenFileAs(app, file, forWrite)).isTrue();
+
+        // Deny
+        if (permission != null) {
+            revokePermission(packageName, permission);
+        } else {
+            denyAppOpsToUid(uid, opstr);
+        }
+        assertThat(canOpenFileAs(app, file, forWrite)).isFalse();
+    }
+
+    @Test
+    public void testSystemGalleryAppHasFullAccessToImages() throws Exception {
+        final File otherAppImageFile = new File(getDcimDir(), "other_" + IMAGE_FILE_NAME);
+        final File topLevelImageFile = new File(getExternalStorageDir(), IMAGE_FILE_NAME);
+        final File imageInAnObviouslyWrongPlace = new File(getMusicDir(), IMAGE_FILE_NAME);
+
+        try {
+            installApp(TEST_APP_A);
+            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+            // Have another app create an image file
+            assertThat(createFileAs(TEST_APP_A, otherAppImageFile.getPath())).isTrue();
+            assertThat(otherAppImageFile.exists()).isTrue();
+
+            // Assert we can write to the file
+            try (FileOutputStream fos = new FileOutputStream(otherAppImageFile)) {
+                fos.write(BYTES_DATA1);
+            }
+
+            // Assert we can read from the file
+            assertFileContent(otherAppImageFile, BYTES_DATA1);
+
+            // Assert we can delete the file
+            assertThat(otherAppImageFile.delete()).isTrue();
+            assertThat(otherAppImageFile.exists()).isFalse();
+
+            // Can create an image anywhere
+            assertCanCreateFile(topLevelImageFile);
+            assertCanCreateFile(imageInAnObviouslyWrongPlace);
+
+            // Put the file back in its place and let TEST_APP_A delete it
+            assertThat(otherAppImageFile.createNewFile()).isTrue();
+        } finally {
+            deleteFileAsNoThrow(TEST_APP_A, otherAppImageFile.getAbsolutePath());
+            otherAppImageFile.delete();
+            uninstallApp(TEST_APP_A);
+            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    @Test
+    public void testSystemGalleryAppHasNoFullAccessToAudio() throws Exception {
+        final File otherAppAudioFile = new File(getMusicDir(), "other_" + AUDIO_FILE_NAME);
+        final File topLevelAudioFile = new File(getExternalStorageDir(), AUDIO_FILE_NAME);
+        final File audioInAnObviouslyWrongPlace = new File(getPicturesDir(), AUDIO_FILE_NAME);
+
+        try {
+            installApp(TEST_APP_A);
+            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+            // Have another app create an audio file
+            assertThat(createFileAs(TEST_APP_A, otherAppAudioFile.getPath())).isTrue();
+            assertThat(otherAppAudioFile.exists()).isTrue();
+
+            // Assert we can't access the file
+            assertThat(canOpen(otherAppAudioFile, /* forWrite */ false)).isFalse();
+            assertThat(canOpen(otherAppAudioFile, /* forWrite */ true)).isFalse();
+
+            // Assert we can't delete the file
+            assertThat(otherAppAudioFile.delete()).isFalse();
+
+            // Can't create an audio file where it doesn't belong
+            assertThrows(IOException.class, "Operation not permitted",
+                    () -> {
+                        topLevelAudioFile.createNewFile();
+                    });
+            assertThrows(IOException.class, "Operation not permitted",
+                    () -> {
+                        audioInAnObviouslyWrongPlace.createNewFile();
+                    });
+        } finally {
+            deleteFileAs(TEST_APP_A, otherAppAudioFile.getPath());
+            uninstallApp(TEST_APP_A);
+            topLevelAudioFile.delete();
+            audioInAnObviouslyWrongPlace.delete();
+            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    @Test
+    public void testSystemGalleryCanRenameImagesAndVideos() throws Exception {
+        final File otherAppVideoFile = new File(getDcimDir(), "other_" + VIDEO_FILE_NAME);
+        final File imageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        final File videoFile = new File(getPicturesDir(), VIDEO_FILE_NAME);
+        final File topLevelVideoFile = new File(getExternalStorageDir(), VIDEO_FILE_NAME);
+        final File musicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
+        try {
+            installApp(TEST_APP_A);
+            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+            // Have another app create a video file
+            assertThat(createFileAs(TEST_APP_A, otherAppVideoFile.getPath())).isTrue();
+            assertThat(otherAppVideoFile.exists()).isTrue();
+
+            // Write some data to the file
+            try (FileOutputStream fos = new FileOutputStream(otherAppVideoFile)) {
+                fos.write(BYTES_DATA1);
+            }
+            assertFileContent(otherAppVideoFile, BYTES_DATA1);
+
+            // Assert we can rename the file and ensure the file has the same content
+            assertCanRenameFile(otherAppVideoFile, videoFile);
+            assertFileContent(videoFile, BYTES_DATA1);
+            // We can even move it to the top level directory
+            assertCanRenameFile(videoFile, topLevelVideoFile);
+            assertFileContent(topLevelVideoFile, BYTES_DATA1);
+            // And we can even convert it into an image file, because why not?
+            assertCanRenameFile(topLevelVideoFile, imageFile);
+            assertFileContent(imageFile, BYTES_DATA1);
+
+            // We can convert it to a music file, but we won't have access to music file after
+            // renaming.
+            assertThat(imageFile.renameTo(musicFile)).isTrue();
+            assertThat(getFileRowIdFromDatabase(musicFile)).isEqualTo(-1);
+        } finally {
+            deleteFileAsNoThrow(TEST_APP_A, otherAppVideoFile.getAbsolutePath());
+            uninstallApp(TEST_APP_A);
+            imageFile.delete();
+            videoFile.delete();
+            topLevelVideoFile.delete();
+            executeShellCommand("rm  " + musicFile.getAbsolutePath());
+            MediaStore.scanFile(getContentResolver(), musicFile);
+            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    /**
+     * Test that basic file path restrictions are enforced on file rename.
+     */
+    @Test
+    public void testRenameFile() throws Exception {
+        final File downloadDir = getDownloadDir();
+        final File nonMediaDir = new File(downloadDir, TEST_DIRECTORY_NAME);
+        final File pdfFile1 = new File(downloadDir, NONMEDIA_FILE_NAME);
+        final File pdfFile2 = new File(nonMediaDir, NONMEDIA_FILE_NAME);
+        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
+        final File videoFile3 = new File(downloadDir, VIDEO_FILE_NAME);
+
+        try {
+            // Renaming non media file to media directory is not allowed.
+            assertThat(pdfFile1.createNewFile()).isTrue();
+            assertCantRenameFile(pdfFile1, new File(getDcimDir(), NONMEDIA_FILE_NAME));
+            assertCantRenameFile(pdfFile1, new File(getMusicDir(), NONMEDIA_FILE_NAME));
+            assertCantRenameFile(pdfFile1, new File(getMoviesDir(), NONMEDIA_FILE_NAME));
+
+            // Renaming non media files to non media directories is allowed.
+            if (!nonMediaDir.exists()) {
+                assertThat(nonMediaDir.mkdirs()).isTrue();
+            }
+            // App can rename pdfFile to non media directory.
+            assertCanRenameFile(pdfFile1, pdfFile2);
+
+            assertThat(videoFile1.createNewFile()).isTrue();
+            // App can rename video file to Movies directory
+            assertCanRenameFile(videoFile1, videoFile2);
+            // App can rename video file to Download directory
+            assertCanRenameFile(videoFile2, videoFile3);
+        } finally {
+            pdfFile1.delete();
+            pdfFile2.delete();
+            videoFile1.delete();
+            videoFile2.delete();
+            videoFile3.delete();
+            nonMediaDir.delete();
+        }
+    }
+
+    /**
+     * Test that renaming file to different mime type is allowed.
+     */
+    @Test
+    public void testRenameFileType() throws Exception {
+        final File pdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        final File videoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+        try {
+            assertThat(pdfFile.createNewFile()).isTrue();
+            assertThat(videoFile.exists()).isFalse();
+            // Moving pdfFile to DCIM directory is not allowed.
+            assertCantRenameFile(pdfFile, new File(getDcimDir(), NONMEDIA_FILE_NAME));
+            // However, moving pdfFile to DCIM directory with changing the mime type to video is
+            // allowed.
+            assertCanRenameFile(pdfFile, videoFile);
+
+            // On rename, MediaProvider database entry for pdfFile should be updated with new
+            // videoFile path and mime type should be updated to video/mp4.
+            assertThat(getFileMimeTypeFromDatabase(videoFile)).isEqualTo("video/mp4");
+        } finally {
+            pdfFile.delete();
+            videoFile.delete();
+        }
+    }
+
+    /**
+     * Test that renaming files overwrites files in newPath.
+     */
+    @Test
+    public void testRenameAndReplaceFile() throws Exception {
+        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
+        final ContentResolver cr = getContentResolver();
+        try {
+            assertThat(videoFile1.createNewFile()).isTrue();
+            assertThat(videoFile2.createNewFile()).isTrue();
+            final Uri uriVideoFile1 = MediaStore.scanFile(cr, videoFile1);
+            final Uri uriVideoFile2 = MediaStore.scanFile(cr, videoFile2);
+
+            // Renaming a file which replaces file in newPath videoFile2 is allowed.
+            assertCanRenameFile(videoFile1, videoFile2);
+
+            // Uri of videoFile2 should be accessible after rename.
+            assertThat(cr.openFileDescriptor(uriVideoFile2, "rw")).isNotNull();
+            // Uri of videoFile1 should not be accessible after rename.
+            assertThrows(FileNotFoundException.class,
+                    () -> {
+                        cr.openFileDescriptor(uriVideoFile1, "rw");
+                    });
+        } finally {
+            videoFile1.delete();
+            videoFile2.delete();
+        }
+    }
+
+    /**
+     * Test that app without write permission for file can't update the file.
+     */
+    @Test
+    public void testRenameFileNotOwned() throws Exception {
+        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
+        try {
+            installApp(TEST_APP_A);
+            assertThat(createFileAs(TEST_APP_A, videoFile1.getAbsolutePath())).isTrue();
+            // App can't rename a file owned by TEST_APP_A.
+            assertCantRenameFile(videoFile1, videoFile2);
+
+            assertThat(videoFile2.createNewFile()).isTrue();
+            // App can't rename a file to videoFile1 which is owned by TEST_APP_A
+            assertCantRenameFile(videoFile2, videoFile1);
+            // TODO(b/146346138): Test that app with right URI permission should be able to rename
+            // the corresponding file
+        } finally {
+            deleteFileAsNoThrow(TEST_APP_A, videoFile1.getAbsolutePath());
+            videoFile2.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that renaming directories is allowed and aligns to default directory restrictions.
+     */
+    @Test
+    public void testRenameDirectory() throws Exception {
+        final File dcimDir = getDcimDir();
+        final File downloadDir = getDownloadDir();
+        final String nonMediaDirectoryName = TEST_DIRECTORY_NAME + "NonMedia";
+        final File nonMediaDirectory = new File(downloadDir, nonMediaDirectoryName);
+        final File pdfFile = new File(nonMediaDirectory, NONMEDIA_FILE_NAME);
+
+        final String mediaDirectoryName = TEST_DIRECTORY_NAME + "Media";
+        final File mediaDirectory1 = new File(dcimDir, mediaDirectoryName);
+        final File videoFile1 = new File(mediaDirectory1, VIDEO_FILE_NAME);
+        final File mediaDirectory2 = new File(downloadDir, mediaDirectoryName);
+        final File videoFile2 = new File(mediaDirectory2, VIDEO_FILE_NAME);
+        final File mediaDirectory3 = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
+        final File videoFile3 = new File(mediaDirectory3, VIDEO_FILE_NAME);
+        final File mediaDirectory4 = new File(mediaDirectory3, mediaDirectoryName);
+
+        try {
+            if (!nonMediaDirectory.exists()) {
+                assertThat(nonMediaDirectory.mkdirs()).isTrue();
+            }
+            assertThat(pdfFile.createNewFile()).isTrue();
+            // Move directory with pdf file to DCIM directory is not allowed.
+            assertThat(nonMediaDirectory.renameTo(new File(dcimDir, nonMediaDirectoryName)))
+                    .isFalse();
+
+            if (!mediaDirectory1.exists()) {
+                assertThat(mediaDirectory1.mkdirs()).isTrue();
+            }
+            assertThat(videoFile1.createNewFile()).isTrue();
+            // Renaming to and from default directories is not allowed.
+            assertThat(mediaDirectory1.renameTo(dcimDir)).isFalse();
+            // Moving top level default directories is not allowed.
+            assertCantRenameDirectory(downloadDir, new File(dcimDir, TEST_DIRECTORY_NAME), null);
+
+            // Moving media directory to Download directory is allowed.
+            assertCanRenameDirectory(mediaDirectory1, mediaDirectory2, new File[] {videoFile1},
+                    new File[] {videoFile2});
+
+            // Moving media directory to Movies directory and renaming directory in new path is
+            // allowed.
+            assertCanRenameDirectory(mediaDirectory2, mediaDirectory3, new File[] {videoFile2},
+                    new File[] {videoFile3});
+
+            // Can't rename a mediaDirectory to non empty non Media directory.
+            assertCantRenameDirectory(mediaDirectory3, nonMediaDirectory, new File[] {videoFile3});
+            // Can't rename a file to a directory.
+            assertCantRenameFile(videoFile3, mediaDirectory3);
+            // Can't rename a directory to file.
+            assertCantRenameDirectory(mediaDirectory3, pdfFile, null);
+            if (!mediaDirectory4.exists()) {
+                assertThat(mediaDirectory4.mkdir()).isTrue();
+            }
+            // Can't rename a directory to subdirectory of itself.
+            assertCantRenameDirectory(mediaDirectory3, mediaDirectory4, new File[] {videoFile3});
+
+        } finally {
+            pdfFile.delete();
+            nonMediaDirectory.delete();
+
+            videoFile1.delete();
+            videoFile2.delete();
+            videoFile3.delete();
+            mediaDirectory1.delete();
+            mediaDirectory2.delete();
+            mediaDirectory3.delete();
+            mediaDirectory4.delete();
+        }
+    }
+
+    /**
+     * Test that renaming directory checks file ownership permissions.
+     */
+    @Test
+    public void testRenameDirectoryNotOwned() throws Exception {
+        final String mediaDirectoryName = TEST_DIRECTORY_NAME + "Media";
+        File mediaDirectory1 = new File(getDcimDir(), mediaDirectoryName);
+        File mediaDirectory2 = new File(getMoviesDir(), mediaDirectoryName);
+        File videoFile = new File(mediaDirectory1, VIDEO_FILE_NAME);
+
+        try {
+            installApp(TEST_APP_A);
+
+            if (!mediaDirectory1.exists()) {
+                assertThat(mediaDirectory1.mkdirs()).isTrue();
+            }
+            assertThat(createFileAs(TEST_APP_A, videoFile.getAbsolutePath())).isTrue();
+            // App doesn't have access to videoFile1, can't rename mediaDirectory1.
+            assertThat(mediaDirectory1.renameTo(mediaDirectory2)).isFalse();
+            assertThat(videoFile.exists()).isTrue();
+            // Test app can delete the file since the file is not moved to new directory.
+            assertThat(deleteFileAs(TEST_APP_A, videoFile.getAbsolutePath())).isTrue();
+        } finally {
+            deleteFileAsNoThrow(TEST_APP_A, videoFile.getAbsolutePath());
+            uninstallAppNoThrow(TEST_APP_A);
+            mediaDirectory1.delete();
+        }
+    }
+
+    /**
+     * Test renaming empty directory is allowed
+     */
+    @Test
+    public void testRenameEmptyDirectory() throws Exception {
+        final String emptyDirectoryName = TEST_DIRECTORY_NAME + "Media";
+        File emptyDirectoryOldPath = new File(getDcimDir(), emptyDirectoryName);
+        File emptyDirectoryNewPath = new File(getMoviesDir(), TEST_DIRECTORY_NAME + "23456");
+        try {
+            if (emptyDirectoryOldPath.exists()) {
+                executeShellCommand("rm -r " + emptyDirectoryOldPath.getPath());
+            }
+            assertThat(emptyDirectoryOldPath.mkdirs()).isTrue();
+            assertCanRenameDirectory(emptyDirectoryOldPath, emptyDirectoryNewPath, null, null);
+        } finally {
+            emptyDirectoryOldPath.delete();
+            emptyDirectoryNewPath.delete();
+        }
+    }
+
+
+    /**
+     * Tests that an instant app can't access external storage.
+     */
+    @Test
+    @AppModeInstant
+    public void testInstantAppsCantAccessExternalStorage() throws Exception {
+        assumeTrue("This test requires that the test runs as an Instant app",
+                getContext().getPackageManager().isInstantApp());
+        assertThat(getContext().getPackageManager().isInstantApp()).isTrue();
+
+        // Can't read ExternalStorageDir
+        assertThat(getExternalStorageDir().list()).isNull();
+
+        // Can't create a top-level direcotry
+        final File topLevelDir = new File(getExternalStorageDir(), TEST_DIRECTORY_NAME);
+        assertThat(topLevelDir.mkdir()).isFalse();
+
+        // Can't create file under root dir
+        final File newTxtFile = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+        assertThrows(IOException.class,
+                () -> {
+                    newTxtFile.createNewFile();
+                });
+
+        // Can't create music file under /MUSIC
+        final File newMusicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
+        assertThrows(IOException.class,
+                () -> {
+                    newMusicFile.createNewFile();
+                });
+
+        // getExternalFilesDir() is not null
+        assertThat(getExternalFilesDir()).isNotNull();
+
+        // Can't read/write app specific dir
+        assertThat(getExternalFilesDir().list()).isNull();
+        assertThat(getExternalFilesDir().exists()).isFalse();
+    }
+
+    /**
+     * Test that apps can create and delete hidden file.
+     */
+    @Test
+    public void testCanCreateHiddenFile() throws Exception {
+        final File hiddenImageFile = new File(getDownloadDir(), ".hiddenFile" + IMAGE_FILE_NAME);
+        try {
+            assertThat(hiddenImageFile.createNewFile()).isTrue();
+            // Write to hidden file is allowed.
+            try (FileOutputStream fos = new FileOutputStream(hiddenImageFile)) {
+                fos.write(BYTES_DATA1);
+            }
+            assertFileContent(hiddenImageFile, BYTES_DATA1);
+
+            assertNotMediaTypeImage(hiddenImageFile);
+
+            assertDirectoryContains(getDownloadDir(), hiddenImageFile);
+            assertThat(getFileRowIdFromDatabase(hiddenImageFile)).isNotEqualTo(-1);
+
+            // We can delete hidden file
+            assertThat(hiddenImageFile.delete()).isTrue();
+            assertThat(hiddenImageFile.exists()).isFalse();
+        } finally {
+            hiddenImageFile.delete();
+        }
+    }
+
+    /**
+     * Test that apps can rename a hidden file.
+     */
+    @Test
+    public void testCanRenameHiddenFile() throws Exception {
+        final String hiddenFileName = ".hidden" + IMAGE_FILE_NAME;
+        final File hiddenImageFile1 = new File(getDcimDir(), hiddenFileName);
+        final File hiddenImageFile2 = new File(getDownloadDir(), hiddenFileName);
+        final File imageFile = new File(getDownloadDir(), IMAGE_FILE_NAME);
+        try {
+            assertThat(hiddenImageFile1.createNewFile()).isTrue();
+            assertCanRenameFile(hiddenImageFile1, hiddenImageFile2);
+            assertNotMediaTypeImage(hiddenImageFile2);
+
+            // We can also rename hidden file to non-hidden
+            assertCanRenameFile(hiddenImageFile2, imageFile);
+            assertIsMediaTypeImage(imageFile);
+
+            // We can rename non-hidden file to hidden
+            assertCanRenameFile(imageFile, hiddenImageFile1);
+            assertNotMediaTypeImage(hiddenImageFile1);
+        } finally {
+            hiddenImageFile1.delete();
+            hiddenImageFile2.delete();
+            imageFile.delete();
+        }
+    }
+
+    /**
+     * Test that files in hidden directory have MEDIA_TYPE=MEDIA_TYPE_NONE
+     */
+    @Test
+    public void testHiddenDirectory() throws Exception {
+        final File hiddenDir = new File(getDownloadDir(), ".hidden" + TEST_DIRECTORY_NAME);
+        final File hiddenImageFile = new File(hiddenDir, IMAGE_FILE_NAME);
+        final File nonHiddenDir = new File(getDownloadDir(), TEST_DIRECTORY_NAME);
+        final File imageFile = new File(nonHiddenDir, IMAGE_FILE_NAME);
+        try {
+            if (!hiddenDir.exists()) {
+                assertThat(hiddenDir.mkdir()).isTrue();
+            }
+            assertThat(hiddenImageFile.createNewFile()).isTrue();
+
+            assertNotMediaTypeImage(hiddenImageFile);
+
+            // Renaming hiddenDir to nonHiddenDir makes the imageFile non-hidden and vice versa
+            assertCanRenameDirectory(
+                    hiddenDir, nonHiddenDir, new File[] {hiddenImageFile}, new File[] {imageFile});
+            assertIsMediaTypeImage(imageFile);
+
+            assertCanRenameDirectory(
+                    nonHiddenDir, hiddenDir, new File[] {imageFile}, new File[] {hiddenImageFile});
+            assertNotMediaTypeImage(hiddenImageFile);
+        } finally {
+            hiddenImageFile.delete();
+            imageFile.delete();
+            hiddenDir.delete();
+            nonHiddenDir.delete();
+        }
+    }
+
+    /**
+     * Test that files in directory with nomedia have MEDIA_TYPE=MEDIA_TYPE_NONE
+     */
+    @Test
+    public void testHiddenDirectory_nomedia() throws Exception {
+        final File directoryNoMedia = new File(getDownloadDir(), "nomedia" + TEST_DIRECTORY_NAME);
+        final File noMediaFile = new File(directoryNoMedia, ".nomedia");
+        final File imageFile = new File(directoryNoMedia, IMAGE_FILE_NAME);
+        final File videoFile = new File(directoryNoMedia, VIDEO_FILE_NAME);
+        try {
+            if (!directoryNoMedia.exists()) {
+                assertThat(directoryNoMedia.mkdir()).isTrue();
+            }
+            assertThat(noMediaFile.createNewFile()).isTrue();
+            assertThat(imageFile.createNewFile()).isTrue();
+
+            assertNotMediaTypeImage(imageFile);
+
+            // Deleting the .nomedia file makes the parent directory non hidden.
+            noMediaFile.delete();
+            MediaStore.scanFile(getContentResolver(), directoryNoMedia);
+            assertIsMediaTypeImage(imageFile);
+
+            // Creating the .nomedia file makes the parent directory hidden again
+            assertThat(noMediaFile.createNewFile()).isTrue();
+            MediaStore.scanFile(getContentResolver(), directoryNoMedia);
+            assertNotMediaTypeImage(imageFile);
+
+            // Renaming the .nomedia file to non hidden file makes the parent directory non hidden.
+            assertCanRenameFile(noMediaFile, videoFile);
+            assertIsMediaTypeImage(imageFile);
+        } finally {
+            noMediaFile.delete();
+            imageFile.delete();
+            videoFile.delete();
+            directoryNoMedia.delete();
+        }
+    }
+
+    /**
+     * Test that only file manager and app that created the hidden file can list it.
+     */
+    @Test
+    public void testListHiddenFile() throws Exception {
+        final File dcimDir = getDcimDir();
+        final String hiddenImageFileName = ".hidden" + IMAGE_FILE_NAME;
+        final File hiddenImageFile = new File(dcimDir, hiddenImageFileName);
+        try {
+            assertThat(hiddenImageFile.createNewFile()).isTrue();
+            assertNotMediaTypeImage(hiddenImageFile);
+
+            assertDirectoryContains(dcimDir, hiddenImageFile);
+
+            installApp(TEST_APP_A, true);
+            // TestApp with read permissions can't see the hidden image file created by other app
+            assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
+                    .doesNotContain(hiddenImageFileName);
+
+            final int testAppUid =
+                    getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
+            // FileManager can see the hidden image file created by other app
+            try {
+                allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+                assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
+                        .contains(hiddenImageFileName);
+            } finally {
+                denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            }
+
+            // Gallery can not see the hidden image file created by other app
+            try {
+                allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+                assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
+                        .doesNotContain(hiddenImageFileName);
+            } finally {
+                denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            }
+        } finally {
+            hiddenImageFile.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testOpenPendingAndTrashed() throws Exception {
+        final File pendingImageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File trashedVideoFile = new File(getPicturesDir(), VIDEO_FILE_NAME);
+        final File pendingPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+        final File trashedPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        Uri pendingImgaeFileUri = null;
+        Uri trashedVideoFileUri = null;
+        Uri pendingPdfFileUri = null;
+        Uri trashedPdfFileUri = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            pendingImgaeFileUri = createPendingFile(pendingImageFile);
+            assertOpenPendingOrTrashed(pendingImgaeFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
+
+            pendingPdfFileUri = createPendingFile(pendingPdfFile);
+            assertOpenPendingOrTrashed(pendingPdfFileUri, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+
+            trashedVideoFileUri = createTrashedFile(trashedVideoFile);
+            assertOpenPendingOrTrashed(trashedVideoFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
+
+            trashedPdfFileUri = createTrashedFile(trashedPdfFile);
+            assertOpenPendingOrTrashed(trashedPdfFileUri, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+
+        } finally {
+            deleteFiles(pendingImageFile, pendingImageFile, trashedVideoFile,
+                    trashedPdfFile);
+            deleteWithMediaProviderNoThrow(pendingImgaeFileUri, trashedVideoFileUri,
+                    pendingPdfFileUri, trashedPdfFileUri);
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testListPendingAndTrashed() throws Exception {
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File pdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        Uri imageFileUri = null;
+        Uri pdfFileUri = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            imageFileUri = createPendingFile(imageFile);
+            // Check that only owner package, file manager and system gallery can list pending image
+            // file.
+            assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
+                    /*isImageOrVideo*/ true);
+
+            trashFile(imageFileUri);
+            // Check that only owner package, file manager and system gallery can list trashed image
+            // file.
+            assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
+                    /*isImageOrVideo*/ true);
+
+            pdfFileUri = createPendingFile(pdfFile);
+            // Check that only owner package, file manager can list pending non media file.
+            assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+
+            trashFile(pdfFileUri);
+            // Check that only owner package, file manager can list trashed non media file.
+            assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+        } finally {
+            deleteWithMediaProviderNoThrow(imageFileUri, pdfFileUri);
+            deleteFiles(imageFile, pdfFile);
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testDeletePendingAndTrashed() throws Exception {
+        final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        final File trashedPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+        // Actual path of the file gets rewritten for pending and trashed files.
+        String pendingVideoFilePath = null;
+        String trashedImageFilePath = null;
+        String pendingPdfFilePath = null;
+        String trashedPdfFilePath = null;
+        try {
+            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
+
+            // App can delete its own pending and trashed file.
+            assertCanDeletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+                    trashedPdfFilePath);
+
+            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
+
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            // App can't delete other app's pending and trashed file.
+            assertCantDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
+                    pendingPdfFilePath, trashedPdfFilePath);
+
+            final int testAppUid =
+                    getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
+            try {
+                allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+                // File Manager can delete any pending and trashed file
+                assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
+                        pendingPdfFilePath, trashedPdfFilePath);
+            } finally {
+                denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            }
+
+            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
+
+            try {
+                allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+                // System Gallery can delete any pending and trashed image or video file.
+                assertTrue(isMediaTypeImageOrVideo(new File(pendingVideoFilePath)));
+                assertTrue(isMediaTypeImageOrVideo(new File(trashedImageFilePath)));
+                assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath);
+
+                // System Gallery can't delete other app's pending and trashed pdf file.
+                assertFalse(isMediaTypeImageOrVideo(new File(pendingPdfFilePath)));
+                assertFalse(isMediaTypeImageOrVideo(new File(trashedPdfFilePath)));
+                assertCantDeletePathsAs(TEST_APP_A, pendingPdfFilePath, trashedPdfFilePath);
+            } finally {
+                denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            }
+        } finally {
+            deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+                    trashedPdfFilePath);
+            deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testQueryOtherAppsFiles() throws Exception {
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
+        try {
+            installApp(TEST_APP_A);
+            // Apps can't query other app's pending file, hence create file and publish it.
+            assertCreatePublishedFilesAs(
+                    TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+
+            // Since the test doesn't have READ_EXTERNAL_STORAGE nor any other special permissions,
+            // it can't query for another app's contents.
+            assertCantQueryFile(otherAppImg);
+            assertCantQueryFile(otherAppMusic);
+            assertCantQueryFile(otherAppPdf);
+            assertCantQueryFile(otherHiddenFile);
+        } finally {
+            deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+            uninstallApp(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testSystemGalleryQueryOtherAppsFiles() throws Exception {
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
+        try {
+            installApp(TEST_APP_A);
+            // Apps can't query other app's pending file, hence create file and publish it.
+            assertCreatePublishedFilesAs(
+                    TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+
+            // System gallery apps have access to video and image files
+            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+            assertCanQueryAndOpenFile(otherAppImg, "rw");
+            // System gallery doesn't have access to hidden image files of other app
+            assertCantQueryFile(otherHiddenFile);
+            // But no access to PDFs or music files
+            assertCantQueryFile(otherAppMusic);
+            assertCantQueryFile(otherAppPdf);
+        } finally {
+            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+            deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+            uninstallApp(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that System Gallery app can rename any directory under the default directories
+     * designated for images and videos, even if they contain other apps' contents that
+     * System Gallery doesn't have read access to.
+     */
+    @Test
+    public void testSystemGalleryCanRenameImageAndVideoDirs() throws Exception {
+        final File dirInDcim = new File(getDcimDir(), TEST_DIRECTORY_NAME);
+        final File dirInPictures = new File(getPicturesDir(), TEST_DIRECTORY_NAME);
+        final File dirInPodcasts = new File(getPodcastsDir(), TEST_DIRECTORY_NAME);
+        final File otherAppImageFile1 = new File(dirInDcim, "other_" + IMAGE_FILE_NAME);
+        final File otherAppVideoFile1 = new File(dirInDcim, "other_" + VIDEO_FILE_NAME);
+        final File otherAppPdfFile1 = new File(dirInDcim, "other_" + NONMEDIA_FILE_NAME);
+        final File otherAppImageFile2 = new File(dirInPictures, "other_" + IMAGE_FILE_NAME);
+        final File otherAppVideoFile2 = new File(dirInPictures, "other_" + VIDEO_FILE_NAME);
+        final File otherAppPdfFile2 = new File(dirInPictures, "other_" + NONMEDIA_FILE_NAME);
+        try {
+            assertThat(dirInDcim.exists() || dirInDcim.mkdir()).isTrue();
+
+            executeShellCommand("touch " + otherAppPdfFile1);
+            MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
+
+            installAppWithStoragePermissions(TEST_APP_A);
+            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+            assertCreateFilesAs(TEST_APP_A, otherAppImageFile1, otherAppVideoFile1);
+
+            // System gallery privileges don't go beyond DCIM, Movies and Pictures boundaries.
+            assertCantRenameDirectory(dirInDcim, dirInPodcasts, /*oldFilesList*/ null);
+
+            // Rename should succeed, but System Gallery still can't access that PDF file!
+            assertCanRenameDirectory(dirInDcim, dirInPictures,
+                    new File[] {otherAppImageFile1, otherAppVideoFile1},
+                    new File[] {otherAppImageFile2, otherAppVideoFile2});
+            assertThat(getFileRowIdFromDatabase(otherAppPdfFile1)).isEqualTo(-1);
+            assertThat(getFileRowIdFromDatabase(otherAppPdfFile2)).isEqualTo(-1);
+        } finally {
+            executeShellCommand("rm " + otherAppPdfFile1);
+            executeShellCommand("rm " + otherAppPdfFile2);
+            MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
+            MediaStore.scanFile(getContentResolver(), otherAppPdfFile2);
+            otherAppImageFile1.delete();
+            otherAppImageFile2.delete();
+            otherAppVideoFile1.delete();
+            otherAppVideoFile2.delete();
+            otherAppPdfFile1.delete();
+            otherAppPdfFile2.delete();
+            dirInDcim.delete();
+            dirInPictures.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    /**
+     * Test that row ID corresponding to deleted path is restored on subsequent create.
+     */
+    @Test
+    public void testCreateCanRestoreDeletedRowId() throws Exception {
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final ContentResolver cr = getContentResolver();
+
+        try {
+            assertThat(imageFile.createNewFile()).isTrue();
+            final long oldRowId = getFileRowIdFromDatabase(imageFile);
+            assertThat(oldRowId).isNotEqualTo(-1);
+            final Uri uriOfOldFile = MediaStore.scanFile(cr, imageFile);
+            assertThat(uriOfOldFile).isNotNull();
+
+            assertThat(imageFile.delete()).isTrue();
+            // We should restore old row Id corresponding to deleted imageFile.
+            assertThat(imageFile.createNewFile()).isTrue();
+            assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(oldRowId);
+            assertThat(cr.openFileDescriptor(uriOfOldFile, "rw")).isNotNull();
+
+            assertThat(imageFile.delete()).isTrue();
+            installApp(TEST_APP_A);
+            assertThat(createFileAs(TEST_APP_A, imageFile.getAbsolutePath())).isTrue();
+
+            final Uri uriOfNewFile = MediaStore.scanFile(getContentResolver(), imageFile);
+            assertThat(uriOfNewFile).isNotNull();
+            // We shouldn't restore deleted row Id if delete & create are called from different apps
+            assertThat(Integer.getInteger(uriOfNewFile.getLastPathSegment()))
+                    .isNotEqualTo(oldRowId);
+        } finally {
+            imageFile.delete();
+            deleteFileAsNoThrow(TEST_APP_A, imageFile.getAbsolutePath());
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that row ID corresponding to deleted path is restored on subsequent rename.
+     */
+    @Test
+    public void testRenameCanRestoreDeletedRowId() throws Exception {
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File temporaryFile = new File(getDownloadDir(), IMAGE_FILE_NAME + "_.tmp");
+        final ContentResolver cr = getContentResolver();
+
+        try {
+            assertThat(imageFile.createNewFile()).isTrue();
+            final Uri oldUri = MediaStore.scanFile(cr, imageFile);
+            assertThat(oldUri).isNotNull();
+
+            Files.copy(imageFile, temporaryFile);
+            assertThat(imageFile.delete()).isTrue();
+            assertCanRenameFile(temporaryFile, imageFile);
+
+            final Uri newUri = MediaStore.scanFile(cr, imageFile);
+            assertThat(newUri).isNotNull();
+            assertThat(newUri.getLastPathSegment()).isEqualTo(oldUri.getLastPathSegment());
+            // oldUri of imageFile is still accessible after delete and rename.
+            assertThat(cr.openFileDescriptor(oldUri, "rw")).isNotNull();
+        } finally {
+            imageFile.delete();
+            temporaryFile.delete();
+        }
+    }
+
+    @Test
+    public void testCantCreateOrRenameFileWithInvalidName() throws Exception {
+        File invalidFile = new File(getDownloadDir(), "<>");
+        File validFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        try {
+            assertThrows(IOException.class, "Operation not permitted",
+                    () -> {
+                        invalidFile.createNewFile();
+                    });
+
+            assertThat(validFile.createNewFile()).isTrue();
+            // We can't rename a file to a file name with invalid FAT characters.
+            assertCantRenameFile(validFile, invalidFile);
+        } finally {
+            invalidFile.delete();
+            validFile.delete();
+        }
+    }
+
+    @Test
+    public void testRenameWithSpecialChars() throws Exception {
+        final String specialCharsSuffix = "'`~!@#$%^& ()_+-={}[];'.)";
+
+        final File fileSpecialChars =
+                new File(getDownloadDir(), NONMEDIA_FILE_NAME + specialCharsSuffix);
+
+        final File dirSpecialChars =
+                new File(getDownloadDir(), TEST_DIRECTORY_NAME + specialCharsSuffix);
+        final File file1 = new File(dirSpecialChars, NONMEDIA_FILE_NAME);
+        final File fileSpecialChars1 =
+                new File(dirSpecialChars, NONMEDIA_FILE_NAME + specialCharsSuffix);
+
+        final File renamedDir = new File(getDocumentsDir(), TEST_DIRECTORY_NAME);
+        final File file2 = new File(renamedDir, NONMEDIA_FILE_NAME);
+        final File fileSpecialChars2 =
+                new File(renamedDir, NONMEDIA_FILE_NAME + specialCharsSuffix);
+        try {
+            assertTrue(fileSpecialChars.createNewFile());
+            if (!dirSpecialChars.exists()) {
+                assertTrue(dirSpecialChars.mkdir());
+            }
+            assertTrue(file1.createNewFile());
+
+            // We can rename file name with special characters
+            assertCanRenameFile(fileSpecialChars, fileSpecialChars1);
+
+            // We can rename directory name with special characters
+            assertCanRenameDirectory(dirSpecialChars, renamedDir,
+                    new File[] {file1, fileSpecialChars1}, new File[] {file2, fileSpecialChars2});
+        } finally {
+            file1.delete();
+            file2.delete();
+            fileSpecialChars.delete();
+            fileSpecialChars1.delete();
+            fileSpecialChars2.delete();
+            dirSpecialChars.delete();
+            renamedDir.delete();
+        }
+    }
+
+    /**
+     * Test that IS_PENDING is set for files created via filepath
+     */
+    @Test
+    public void testPendingFromFuse() throws Exception {
+        final File pendingFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File otherPendingFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+        try {
+            assertTrue(pendingFile.createNewFile());
+            // Newly created file should have IS_PENDING set
+            try (Cursor c = queryFile(pendingFile, MediaStore.MediaColumns.IS_PENDING)) {
+                assertTrue(c.moveToFirst());
+                assertThat(c.getInt(0)).isEqualTo(1);
+            }
+
+            // If we query with MATCH_EXCLUDE, we should still see this pendingFile
+            try (Cursor c = queryFileExcludingPending(pendingFile,
+                    MediaStore.MediaColumns.IS_PENDING)) {
+                assertThat(c.getCount()).isEqualTo(1);
+                assertTrue(c.moveToFirst());
+                assertThat(c.getInt(0)).isEqualTo(1);
+            }
+
+            assertNotNull(MediaStore.scanFile(getContentResolver(), pendingFile));
+
+            // IS_PENDING should be unset after the scan
+            try (Cursor c = queryFile(pendingFile, MediaStore.MediaColumns.IS_PENDING)) {
+                assertTrue(c.moveToFirst());
+                assertThat(c.getInt(0)).isEqualTo(0);
+            }
+
+            installAppWithStoragePermissions(TEST_APP_A);
+            assertCreateFilesAs(TEST_APP_A, otherPendingFile);
+            // We can't query other apps pending file from FUSE with MATCH_EXCLUDE
+            try (Cursor c = queryFileExcludingPending(otherPendingFile,
+                    MediaStore.MediaColumns.IS_PENDING)) {
+                assertThat(c.getCount()).isEqualTo(0);
+            }
+        } finally {
+            pendingFile.delete();
+            deleteFileAsNoThrow(TEST_APP_A, otherPendingFile.getAbsolutePath());
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * Test that apps can't set attributes on another app's files.
+     */
+    @Test
+    public void testCantSetAttrOtherAppsFile() throws Exception {
+        // This path's permission is checked in MediaProvider (directory/external media dir)
+        final File externalMediaPath = new File(getExternalMediaDir(), VIDEO_FILE_NAME);
+
+        try {
+            // Create the files
+            if (!externalMediaPath.exists()) {
+                assertThat(externalMediaPath.createNewFile()).isTrue();
+            }
+
+            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            // TEST_APP_A should not be able to setattr to other app's files.
+            assertWithMessage(
+                    "setattr on directory/external media path [%s]", externalMediaPath.getPath())
+                    .that(setAttrAs(TEST_APP_A, externalMediaPath.getPath()))
+                    .isFalse();
+        } finally {
+            externalMediaPath.delete();
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    /**
+     * b/171768780: Test that scan doesn't skip scanning renamed hidden file.
+     */
+    @Test
+    public void testScanUpdatesMetadataForRenamedHiddenFile() throws Exception {
+        final File hiddenFile = new File(getPicturesDir(), ".hidden_" + IMAGE_FILE_NAME);
+        final File jpgFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        try {
+            // Copy the image content to hidden file
+            try (InputStream in =
+                         getContext().getResources().openRawResource(R.raw.img_with_metadata);
+                 FileOutputStream out = new FileOutputStream(hiddenFile)) {
+                FileUtils.copy(in, out);
+                out.getFD().sync();
+            }
+            Uri scanUri = MediaStore.scanFile(getContentResolver(), hiddenFile);
+            assertNotNull(scanUri);
+
+            // Rename hidden file to non-hidden
+            assertCanRenameFile(hiddenFile, jpgFile);
+
+            try (Cursor c = queryFile(jpgFile, MediaStore.MediaColumns.DATE_TAKEN)) {
+                assertTrue(c.moveToFirst());
+                // The file is not scanned yet, hence the metadata is not updated yet.
+                assertThat(c.getString(0)).isNull();
+            }
+
+            // Scan the file to update the metadata for renamed hidden file.
+            scanUri = MediaStore.scanFile(getContentResolver(), jpgFile);
+            assertNotNull(scanUri);
+
+            // Scan should be able to update metadata even if File.lastModifiedTime hasn't changed.
+            try (Cursor c = queryFile(jpgFile, MediaStore.MediaColumns.DATE_TAKEN)) {
+                assertTrue(c.moveToFirst());
+                assertThat(c.getString(0)).isNotNull();
+            }
+        } finally {
+            hiddenFile.delete();
+            jpgFile.delete();
+        }
+    }
+
+    /**
+     * Checks restrictions for opening pending and trashed files by different apps. Assumes that
+     * given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
+     * method doesn't uninstall given {@code testApp} at the end.
+     */
+    private void assertOpenPendingOrTrashed(Uri uri, TestApp testApp, boolean isImageOrVideo)
+            throws Exception {
+        final File pendingOrTrashedFile = new File(getFilePathFromUri(uri));
+
+        // App can open its pending or trashed file for read or write
+        assertTrue(canOpen(pendingOrTrashedFile, /*forWrite*/ false));
+        assertTrue(canOpen(pendingOrTrashedFile, /*forWrite*/ true));
+
+        // App with READ_EXTERNAL_STORAGE can't open other app's pending or trashed file for read or
+        // write
+        assertFalse(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+        assertFalse(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+
+        final int testAppUid =
+                getContext().getPackageManager().getPackageUid(testApp.getPackageName(), 0);
+        try {
+            allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            // File Manager can open any pending or trashed file for read or write
+            assertTrue(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+            assertTrue(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+        } finally {
+            denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+        }
+
+        try {
+            allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            if (isImageOrVideo) {
+                // System Gallery can open any pending or trashed image/video file for read or write
+                assertTrue(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertTrue(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+                assertTrue(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+            } else {
+                // System Gallery can't open other app's pending or trashed non-media file for read
+                // or write
+                assertFalse(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertFalse(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+                assertFalse(canOpenFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+            }
+        } finally {
+            denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    /**
+     * Checks restrictions for listing pending and trashed files by different apps. Assumes that
+     * given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
+     * method doesn't uninstall given {@code testApp} at the end.
+     */
+    private void assertListPendingOrTrashed(Uri uri, File file, TestApp testApp,
+            boolean isImageOrVideo) throws Exception {
+        final String parentDirPath = file.getParent();
+        assertTrue(new File(parentDirPath).isDirectory());
+
+        final List<String> listedFileNames = Arrays.asList(new File(parentDirPath).list());
+        assertThat(listedFileNames).doesNotContain(file);
+
+        final File pendingOrTrashedFile = new File(getFilePathFromUri(uri));
+
+        assertThat(listedFileNames).contains(pendingOrTrashedFile.getName());
+
+        // App with READ_EXTERNAL_STORAGE can't see other app's pending or trashed file.
+        assertThat(listAs(testApp, parentDirPath)).doesNotContain(pendingOrTrashedFile.getName());
+
+        final int testAppUid =
+                getContext().getPackageManager().getPackageUid(testApp.getPackageName(), 0);
+        try {
+            allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            // File Manager can see any pending or trashed file.
+            assertThat(listAs(testApp, parentDirPath)).contains(pendingOrTrashedFile.getName());
+        } finally {
+            denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+        }
+
+        try {
+            allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            if (isImageOrVideo) {
+                // System Gallery can see any pending or trashed image/video file.
+                assertTrue(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertThat(listAs(testApp, parentDirPath)).contains(pendingOrTrashedFile.getName());
+            } else {
+                // System Gallery can't see other app's pending or trashed non media file.
+                assertFalse(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertThat(listAs(testApp, parentDirPath))
+                        .doesNotContain(pendingOrTrashedFile.getName());
+            }
+        } finally {
+            denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    private Uri createPendingFile(File pendingFile) throws Exception {
+        assertTrue(pendingFile.createNewFile());
+
+        final ContentResolver cr = getContentResolver();
+        final Uri trashedFileUri = MediaStore.scanFile(cr, pendingFile);
+        assertNotNull(trashedFileUri);
+
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.IS_PENDING, 1);
+        assertEquals(1, cr.update(trashedFileUri, values, Bundle.EMPTY));
+
+        return trashedFileUri;
+    }
+
+    private Uri createTrashedFile(File trashedFile) throws Exception {
+        assertTrue(trashedFile.createNewFile());
+
+        final ContentResolver cr = getContentResolver();
+        final Uri trashedFileUri = MediaStore.scanFile(cr, trashedFile);
+        assertNotNull(trashedFileUri);
+
+        trashFile(trashedFileUri);
+        return trashedFileUri;
+    }
+
+    private void trashFile(Uri uri) throws Exception {
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.IS_TRASHED, 1);
+        assertEquals(1, getContentResolver().update(uri, values, Bundle.EMPTY));
+    }
+
+    /**
+     * Gets file path corresponding to the db row pointed by {@code uri}. If {@code uri} points to
+     * multiple db rows, file path is extracted from the first db row of the database query result.
+     */
+    private String getFilePathFromUri(Uri uri) {
+        final String[] projection = new String[] {MediaStore.MediaColumns.DATA};
+        try (Cursor c = getContentResolver().query(uri, projection, null, null)) {
+            assertTrue(c.moveToFirst());
+            return c.getString(0);
+        }
+    }
+
+    private boolean isMediaTypeImageOrVideo(File file) {
+        return queryImageFile(file).getCount() == 1 || queryVideoFile(file).getCount() == 1;
+    }
+
+    private static void assertIsMediaTypeImage(File file) {
+        final Cursor c = queryImageFile(file);
+        assertEquals(1, c.getCount());
+    }
+
+    private static void assertNotMediaTypeImage(File file) {
+        final Cursor c = queryImageFile(file);
+        assertEquals(0, c.getCount());
+    }
+
+    private static void assertCantQueryFile(File file) {
+        assertThat(getFileUri(file)).isNull();
+        // Confirm that file exists in the database.
+        assertNotNull(MediaStore.scanFile(getContentResolver(), file));
+    }
+
+    private static void assertCreateFilesAs(TestApp testApp, File... files) throws Exception {
+        for (File file : files) {
+            assertFalse("File already exists: " + file, file.exists());
+            assertTrue("Failed to create file " + file + " on behalf of "
+                    + testApp.getPackageName(), createFileAs(testApp, file.getPath()));
+        }
+    }
+
+    /**
+     * Makes {@code testApp} create {@code files}. Publishes {@code files} by scanning the file.
+     * Pending files from FUSE are not visible to other apps via MediaStore APIs. We have to publish
+     * the file or make the file non-pending to make the file visible to other apps.
+     * <p>
+     * Note that this method can only be used for scannable files.
+     */
+    private static void assertCreatePublishedFilesAs(TestApp testApp, File... files)
+            throws Exception {
+        for (File file : files) {
+            assertTrue("Failed to create published file " + file + " on behalf of "
+                    + testApp.getPackageName(), createFileAs(testApp, file.getPath()));
+            assertNotNull("Failed to scan " + file,
+                    MediaStore.scanFile(getContentResolver(), file));
+        }
+    }
+
+
+    private static void deleteFilesAs(TestApp testApp, File... files) throws Exception {
+        for (File file : files) {
+            deleteFileAs(testApp, file.getPath());
+        }
+    }
+    private static void assertCanDeletePathsAs(TestApp testApp, String... filePaths)
+            throws Exception {
+        for (String path: filePaths) {
+            assertTrue("Failed to delete file " + path + " on behalf of "
+                    + testApp.getPackageName(), deleteFileAs(testApp, path));
+        }
+    }
+
+    private static void assertCantDeletePathsAs(TestApp testApp, String... filePaths)
+            throws Exception {
+        for (String path: filePaths) {
+            assertFalse("Deleting " + path + " on behalf of " + testApp.getPackageName()
+                    + " was expected to fail", deleteFileAs(testApp, path));
+        }
+    }
+
+    private void deleteFiles(File... files) {
+        for (File file: files) {
+            if (file == null) continue;
+            file.delete();
+        }
+    }
+
+    private void deletePaths(String... paths) {
+        for (String path: paths) {
+            if (path == null) continue;
+            new File(path).delete();
+        }
+    }
+
+    private static void assertCanDeletePaths(String... filePaths) {
+        for (String filePath : filePaths) {
+            assertTrue("Failed to delete " + filePath,
+                    new File(filePath).delete());
+        }
+    }
+
+    /**
+     * For possible values of {@code mode}, look at {@link android.content.ContentProvider#openFile}
+     */
+    private static void assertCanQueryAndOpenFile(File file, String mode) throws IOException {
+        // This call performs the query
+        final Uri fileUri = getFileUri(file);
+        // The query succeeds iff it didn't return null
+        assertThat(fileUri).isNotNull();
+        // Now we assert that we can open the file through ContentResolver
+        try (ParcelFileDescriptor pfd =
+                     getContentResolver().openFileDescriptor(fileUri, mode)) {
+            assertThat(pfd).isNotNull();
+        }
+    }
+
+    /**
+     * Assert that the last read in: read - write - read using {@code readFd} and {@code writeFd}
+     * see the last write. {@code readFd} and {@code writeFd} are fds pointing to the same
+     * underlying file on disk but may be derived from different mount points and in that case
+     * have separate VFS caches.
+     */
+    private void assertRWR(ParcelFileDescriptor readPfd, ParcelFileDescriptor writePfd)
+            throws Exception {
+        FileDescriptor readFd = readPfd.getFileDescriptor();
+        FileDescriptor writeFd = writePfd.getFileDescriptor();
+
+        byte[] readBuffer = new byte[10];
+        byte[] writeBuffer = new byte[10];
+        Arrays.fill(writeBuffer, (byte) 1);
+
+        // Write so readFd has content to read from next
+        Os.pwrite(readFd, readBuffer, 0, 10, 0);
+        // Read so readBuffer is in readFd's mount VFS cache
+        Os.pread(readFd, readBuffer, 0, 10, 0);
+
+        // Assert that readBuffer is zeroes
+        assertThat(readBuffer).isEqualTo(new byte[10]);
+
+        // Write so writeFd and readFd should now see writeBuffer
+        Os.pwrite(writeFd, writeBuffer, 0, 10, 0);
+
+        // Read so the last write can be verified on readFd
+        Os.pread(readFd, readBuffer, 0, 10, 0);
+
+        // Assert that the last write is indeed visible via readFd
+        assertThat(readBuffer).isEqualTo(writeBuffer);
+        assertThat(readPfd.getStatSize()).isEqualTo(writePfd.getStatSize());
+    }
+
+    private void assertLowerFsFd(ParcelFileDescriptor pfd) throws Exception {
+        assertThat(Os.readlink("/proc/self/fd/" + pfd.getFd()).startsWith("/storage")).isTrue();
+    }
+
+    private void assertUpperFsFd(ParcelFileDescriptor pfd) throws Exception {
+        assertThat(Os.readlink("/proc/self/fd/" + pfd.getFd()).startsWith("/mnt/user")).isTrue();
+    }
+
+    private static void assertCanCreateFile(File file) throws IOException {
+        // If the file somehow managed to survive a previous run, then the test app was uninstalled
+        // and MediaProvider will remove our its ownership of the file, so it's not guaranteed that
+        // we can create nor delete it.
+        if (!file.exists()) {
+            assertThat(file.createNewFile()).isTrue();
+            assertThat(file.delete()).isTrue();
+        } else {
+            Log.w(TAG,
+                    "Couldn't assertCanCreateFile(" + file + ") because file existed prior to "
+                            + "running the test!");
+        }
+    }
+
+    private static void assertAccess(File file, boolean exists, boolean canRead, boolean canWrite)
+            throws Exception {
+        assertAccess(file, exists, canRead, canWrite, true /* checkExists */);
+    }
+
+    private static void assertAccess(File file, boolean exists, boolean canRead, boolean canWrite,
+            boolean checkExists) throws Exception {
+        if (checkExists) {
+            assertThat(file.exists()).isEqualTo(exists);
+        }
+        assertThat(file.canRead()).isEqualTo(canRead);
+        assertThat(file.canWrite()).isEqualTo(canWrite);
+        if (file.isDirectory()) {
+            if (checkExists) {
+                assertThat(file.canExecute()).isEqualTo(exists);
+            }
+        } else {
+            assertThat(file.canExecute()).isFalse(); // Filesytem is mounted with MS_NOEXEC
+        }
+
+        // Test some combinations of mask.
+        assertAccess(file, R_OK, canRead);
+        assertAccess(file, W_OK, canWrite);
+        assertAccess(file, R_OK | W_OK, canRead && canWrite);
+        assertAccess(file, W_OK | F_OK, canWrite);
+
+        if (checkExists) {
+            assertAccess(file, F_OK, exists);
+        }
+    }
+
+    private static void assertAccess(File file, int mask, boolean expected) throws Exception {
+        if (expected) {
+            assertThat(Os.access(file.getAbsolutePath(), mask)).isTrue();
+        } else {
+            assertThrows(ErrnoException.class, () -> {
+                Os.access(file.getAbsolutePath(), mask);
+            });
+        }
     }
 }
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStoragePublicVolumeDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStoragePublicVolumeDeviceTest.java
index aa2f24f..8856887 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStoragePublicVolumeDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStoragePublicVolumeDeviceTest.java
@@ -23,8 +23,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.Test;
 import org.junit.runner.RunWith;
 
 /**
@@ -34,21 +34,21 @@
 public class ScopedStoragePublicVolumeDeviceTest extends ScopedStorageDeviceTest {
 
     @BeforeClass
-    public static void setupPublicVolume() throws Exception {
-        TestUtils.createNewPublicVolume();
-        final String volumeName = TestUtils.getPublicVolumeName();
-        assertThat(volumeName).isNotNull();
-        TestUtils.setExternalStorageVolume(volumeName);
-    }
-
-    @Test
-    public void verifySetup() {
-        assertThat(TestUtils.getExternalFilesDir().getAbsolutePath().indexOf("emulated"))
-                .isEqualTo(-1);
+    public static void createPublicVolume() throws Exception {
+        ScopedStorageDeviceTest.createPublicVolume();
     }
 
     @AfterClass
-    public static void deletePublicVolumes() throws Exception {
+    public static void resetDefaultVolume() throws Exception {
         TestUtils.resetDefaultExternalStorageVolume();
     }
+
+    @Before
+    public void setup() throws Exception {
+        String volumeName = TestUtils.getPublicVolumeName();
+        assertThat(volumeName).isNotNull();
+        TestUtils.setExternalStorageVolume(volumeName);
+        TestUtils.assertDefaultVolumeIsPublic();
+        super.setup();
+    }
 }
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageCoreHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageCoreHostTest.java
index a999fb5..b38c193 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageCoreHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageCoreHostTest.java
@@ -80,103 +80,6 @@
     }
 
     @Test
-    public void testTypePathConformity() throws Exception {
-        runDeviceTest("testTypePathConformity");
-    }
-
-    @Test
-    public void testCreateFileInAppExternalDir() throws Exception {
-        runDeviceTest("testCreateFileInAppExternalDir");
-    }
-
-    @Test
-    public void testCreateFileInOtherAppExternalDir() throws Exception {
-        runDeviceTest("testCreateFileInOtherAppExternalDir");
-    }
-
-    @Test
-    public void testContributeMediaFile() throws Exception {
-        runDeviceTest("testContributeMediaFile");
-    }
-
-    @Test
-    public void testCreateAndDeleteEmptyDir() throws Exception {
-        runDeviceTest("testCreateAndDeleteEmptyDir");
-    }
-
-    @Test
-    public void testOpendirRestrictions() throws Exception {
-        runDeviceTest("testOpendirRestrictions");
-    }
-
-    @Test
-    public void testLowLevelFileIO() throws Exception {
-        runDeviceTest("testLowLevelFileIO");
-    }
-
-    @Test
-    public void testListDirectoriesWithMediaFiles() throws Exception {
-        runDeviceTest("testListDirectoriesWithMediaFiles");
-    }
-
-    @Test
-    public void testListFilesFromExternalMediaDirectory() throws Exception {
-        runDeviceTest("testListFilesFromExternalMediaDirectory");
-    }
-
-    @Test
-    public void testMetaDataRedaction() throws Exception {
-        runDeviceTest("testMetaDataRedaction");
-    }
-
-    @Test
-    public void testVfsCacheConsistency() throws Exception {
-        runDeviceTest("testOpenFilePathFirstWriteContentResolver");
-        runDeviceTest("testOpenContentResolverFirstWriteContentResolver");
-        runDeviceTest("testOpenFilePathFirstWriteFilePath");
-        runDeviceTest("testOpenContentResolverFirstWriteFilePath");
-        runDeviceTest("testOpenContentResolverWriteOnly");
-        runDeviceTest("testOpenContentResolverDup");
-        runDeviceTest("testContentResolverDelete");
-        runDeviceTest("testContentResolverUpdate");
-        runDeviceTest("testOpenContentResolverClose");
-    }
-
-    @Test
-    public void testCaseInsensitivity() throws Exception {
-        runDeviceTest("testCreateLowerCaseDeleteUpperCase");
-        runDeviceTest("testCreateUpperCaseDeleteLowerCase");
-        runDeviceTest("testCreateMixedCaseDeleteDifferentMixedCase");
-        runDeviceTest("testAndroidDataObbDoesNotForgetMount");
-        runDeviceTest("testCacheConsistencyForCaseInsensitivity");
-    }
-
-    @Test
-    public void testRenameAndReplaceFile() throws Exception {
-        runDeviceTest("testRenameAndReplaceFile");
-    }
-
-    @Test
-    public void testRenameDirectory() throws Exception {
-        runDeviceTest("testRenameDirectory");
-    }
-
-    @Test
-    public void testSystemGalleryAppHasFullAccessToImages() throws Exception {
-        runDeviceTest("testSystemGalleryAppHasFullAccessToImages");
-    }
-
-    @Test
-    public void testSystemGalleryAppHasNoFullAccessToAudio() throws Exception {
-        runDeviceTest("testSystemGalleryAppHasNoFullAccessToAudio");
-    }
-
-    @Test
-    public void testSystemGalleryCanRenameImageAndVideoDirs() throws Exception {
-        runDeviceTest("testSystemGalleryCanRenameImageAndVideoDirs");
-    }
-
-    @Test
     public void testManageExternalStorageCanCreateFilesAnywhere() throws Exception {
         allowAppOps("android:manage_external_storage");
         try {
@@ -197,28 +100,6 @@
     }
 
     @Test
-    public void testHiddenFiles() throws Exception {
-        runDeviceTest("testCanCreateHiddenFile");
-        runDeviceTest("testCanRenameHiddenFile");
-        runDeviceTest("testHiddenDirectory");
-    }
-
-    @Test
-    public void testCreateCanRestoreDeletedRowId() throws Exception {
-        runDeviceTest("testCreateCanRestoreDeletedRowId");
-    }
-
-    @Test
-    public void testRenameCanRestoreDeletedRowId() throws Exception {
-        runDeviceTest("testRenameCanRestoreDeletedRowId");
-    }
-
-    @Test
-    public void testQueryOtherAppsFiles() throws Exception {
-        runDeviceTest("testQueryOtherAppsFiles");
-    }
-
-    @Test
     public void testAccess_file() throws Exception {
         grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
         try {
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index 5522702..88f5242 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -93,31 +93,6 @@
         executeShellCommand("rm -r /sdcard/Android/data/com.android.shell");
     }
 
-    @Test
-    public void testReadWriteFilesInOtherAppExternalDir() throws Exception {
-        runDeviceTest("testReadWriteFilesInOtherAppExternalDir");
-    }
-
-    @Test
-    public void testCantDeleteOtherAppsContents() throws Exception {
-        runDeviceTest("testCantDeleteOtherAppsContents");
-    }
-
-    @Test
-    public void testDeleteAlreadyUnlinkedFile() throws Exception {
-        runDeviceTest("testDeleteAlreadyUnlinkedFile");
-
-    }
-
-    @Test
-    public void testListDirectoriesWithNonMediaFiles() throws Exception {
-        runDeviceTest("testListDirectoriesWithNonMediaFiles");
-    }
-
-    @Test
-    public void testListFilesFromExternalFilesDirectory() throws Exception {
-        runDeviceTest("testListFilesFromExternalFilesDirectory");
-    }
 
     @Test
     public void testListUnsupportedFileType() throws Exception {
@@ -125,48 +100,6 @@
     }
 
     @Test
-    public void testCallingIdentityCacheInvalidation() throws Exception {
-        // General IO access
-        runDeviceTest("testReadStorageInvalidation");
-        runDeviceTest("testWriteStorageInvalidation");
-        // File manager access
-        runDeviceTest("testManageStorageInvalidation");
-        // Default gallery
-        runDeviceTest("testWriteImagesInvalidation");
-        runDeviceTest("testWriteVideoInvalidation");
-        // EXIF access
-        runDeviceTest("testAccessMediaLocationInvalidation");
-
-        runDeviceTest("testAppUpdateInvalidation");
-        runDeviceTest("testAppReinstallInvalidation");
-    }
-
-    @Test
-    public void testRenameFile() throws Exception {
-        runDeviceTest("testRenameFile");
-    }
-
-    @Test
-    public void testRenameFileType() throws Exception {
-        runDeviceTest("testRenameFileType");
-    }
-
-    @Test
-    public void testRenameFileNotOwned() throws Exception {
-        runDeviceTest("testRenameFileNotOwned");
-    }
-
-    @Test
-    public void testRenameDirectoryNotOwned() throws Exception {
-        runDeviceTest("testRenameDirectoryNotOwned");
-    }
-
-    @Test
-    public void testRenameEmptyDirectory() throws Exception {
-        runDeviceTest("testRenameEmptyDirectory");
-    }
-
-    @Test
     public void testCantRenameToTopLevelDirectory() throws Exception {
         runDeviceTest("testCantRenameToTopLevelDirectory");
     }
@@ -202,46 +135,6 @@
     }
 
     @Test
-    public void testCantAccessOtherAppsContents() throws Exception {
-        runDeviceTest("testCantAccessOtherAppsContents");
-    }
-
-    @Test
-    public void testSystemGalleryCanRenameImagesAndVideos() throws Exception {
-        runDeviceTest("testSystemGalleryCanRenameImagesAndVideos");
-    }
-
-    @Test
-    public void testCanWriteToDCIMCameraWithNomedia() throws Exception {
-        runDeviceTest("testCanWriteToDCIMCameraWithNomedia");
-    }
-
-    @Test
-    public void testHiddenDirectory_nomedia() throws Exception {
-        runDeviceTest("testHiddenDirectory_nomedia");
-    }
-
-    @Test
-    public void testListHiddenFile() throws Exception {
-        runDeviceTest("testListHiddenFile");
-    }
-
-    @Test
-    public void testOpenPendingAndTrashed() throws Exception {
-        runDeviceTest("testOpenPendingAndTrashed");
-    }
-
-    @Test
-    public void testDeletePendingAndTrashed() throws Exception {
-        runDeviceTest("testDeletePendingAndTrashed");
-    }
-
-    @Test
-    public void testListPendingAndTrashed() throws Exception {
-        runDeviceTest("testListPendingAndTrashed");
-    }
-
-    @Test
     public void testCanCreateDefaultDirectory() throws Exception {
         runDeviceTest("testCanCreateDefaultDirectory");
     }
@@ -257,26 +150,6 @@
     }
 
     @Test
-    public void testSystemGalleryQueryOtherAppsFiles() throws Exception {
-        runDeviceTest("testSystemGalleryQueryOtherAppsFiles");
-    }
-
-    @Test
-    public void testCantCreateOrRenameFileWithInvalidName() throws Exception {
-        runDeviceTest("testCantCreateOrRenameFileWithInvalidName");
-    }
-
-    @Test
-    public void testRenameWithSpecialChars() throws Exception {
-        runDeviceTest("testRenameWithSpecialChars");
-    }
-
-    @Test
-    public void testPendingFromFuse() throws Exception {
-        runDeviceTest("testPendingFromFuse");
-    }
-
-    @Test
     public void testOpenOtherPendingFilesFromFuse() throws Exception {
         grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
         try {
@@ -287,11 +160,6 @@
     }
 
     @Test
-    public void testCantSetAttrOtherAppsFile() throws Exception {
-        runDeviceTest("testCantSetAttrOtherAppsFile");
-    }
-
-    @Test
     public void testAndroidMedia() throws Exception {
         grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
         try {
@@ -338,11 +206,6 @@
                 "testNoIsolatedStorageCantReadWriteOtherAppExternalDir");
         runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageStorageReaddir");
         runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageQueryOtherAppsFile");
-
-        // Check that appop is revoked after instrumentation is over.
-        runDeviceTest("testCreateFileInAppExternalDir");
-        runDeviceTest("testCreateFileInOtherAppExternalDir");
-        runDeviceTest("testReadWriteFilesInOtherAppExternalDir");
     }
 
     @Test
@@ -362,11 +225,6 @@
     }
 
     @Test
-    public void testScanUpdatesMetadataForRenamedHiddenFile() throws Exception {
-        runDeviceTest("testScanUpdatesMetadataForRenamedHiddenFile");
-    }
-
-    @Test
     public void testClearPackageData() throws Exception {
         grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
         try {
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 c3029c9..bb4ec8d 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
@@ -787,6 +787,20 @@
     }
 
     /**
+     * Asserts the default volume used in helper methods is the primary volume.
+     */
+    public static void assertDefaultVolumeIsPrimary() {
+        assertVolumeType(true /* isPrimary */);
+    }
+
+    /**
+     * Asserts the default volume used in helper methods is a public volume.
+     */
+    public static void assertDefaultVolumeIsPublic() {
+        assertVolumeType(false /* isPrimary */);
+    }
+
+    /**
      * Creates and returns the Android data sub-directory belonging to the calling package.
      */
     public static File getExternalFilesDir() {
@@ -1098,19 +1112,22 @@
     }
 
     /**
-     * Gets the name of the public volume.
+     * Gets the name of the public volume, waiting for a bit for it to be available.
      */
     public static String getPublicVolumeName() throws Exception {
         final String[] volName = new String[1];
         pollForCondition(() -> {
-            volName[0] = getPublicVolumeNameInternal();
+            volName[0] = getCurrentPublicVolumeName();
             return volName[0] != null;
         }, "Timed out while waiting for public volume to be ready");
 
         return volName[0];
     }
 
-    private static String getPublicVolumeNameInternal() {
+    /**
+     * @return the currently mounted public volume, if any.
+     */
+    public static String getCurrentPublicVolumeName() {
         final String[] allVolumeDetails;
         try {
             allVolumeDetails = executeShellCommand("sm list-volumes")
@@ -1158,4 +1175,15 @@
                 () -> Environment.isExternalStorageManager(),
                 "Timed out while waiting for MANAGE_EXTERNAL_STORAGE");
     }
+
+    private static void assertVolumeType(boolean isPrimary) {
+        String[] parts = getExternalFilesDir().getAbsolutePath().split("/");
+        assertThat(parts.length).isAtLeast(3);
+        assertThat(parts[1]).isEqualTo("storage");
+        if (isPrimary) {
+            assertThat(parts[2]).isEqualTo("emulated");
+        } else {
+            assertThat(parts[2]).isNotEqualTo("emulated");
+        }
+    }
 }
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index cae78ef..0df8d43 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -19,20 +19,12 @@
 import static android.app.AppOpsManager.permissionToOp;
 import static android.os.SystemProperties.getBoolean;
 import static android.provider.MediaStore.MediaColumns;
-import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMatch;
-import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMismatch;
-import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata;
-import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromRawResource;
 import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA1;
-import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA2;
-import static android.scopedstorage.cts.lib.TestUtils.STR_DATA1;
-import static android.scopedstorage.cts.lib.TestUtils.STR_DATA2;
 import static android.scopedstorage.cts.lib.TestUtils.adoptShellPermissionIdentity;
 import static android.scopedstorage.cts.lib.TestUtils.allowAppOpsToUid;
 import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameDirectory;
 import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameFile;
 import static android.scopedstorage.cts.lib.TestUtils.assertCantRenameDirectory;
-import static android.scopedstorage.cts.lib.TestUtils.assertCantRenameFile;
 import static android.scopedstorage.cts.lib.TestUtils.assertDirectoryContains;
 import static android.scopedstorage.cts.lib.TestUtils.assertFileContent;
 import static android.scopedstorage.cts.lib.TestUtils.assertThrows;
@@ -42,38 +34,27 @@
 import static android.scopedstorage.cts.lib.TestUtils.createFileAs;
 import static android.scopedstorage.cts.lib.TestUtils.deleteFileAs;
 import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow;
-import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively;
-import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProvider;
 import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow;
 import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid;
 import static android.scopedstorage.cts.lib.TestUtils.dropShellPermissionIdentity;
 import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand;
-import static android.scopedstorage.cts.lib.TestUtils.getAlarmsDir;
-import static android.scopedstorage.cts.lib.TestUtils.getAndroidDataDir;
 import static android.scopedstorage.cts.lib.TestUtils.getAndroidDir;
 import static android.scopedstorage.cts.lib.TestUtils.getAndroidMediaDir;
-import static android.scopedstorage.cts.lib.TestUtils.getAudiobooksDir;
 import static android.scopedstorage.cts.lib.TestUtils.getContentResolver;
 import static android.scopedstorage.cts.lib.TestUtils.getDcimDir;
 import static android.scopedstorage.cts.lib.TestUtils.getDefaultTopLevelDirs;
-import static android.scopedstorage.cts.lib.TestUtils.getDocumentsDir;
 import static android.scopedstorage.cts.lib.TestUtils.getDownloadDir;
 import static android.scopedstorage.cts.lib.TestUtils.getExternalFilesDir;
 import static android.scopedstorage.cts.lib.TestUtils.getExternalMediaDir;
 import static android.scopedstorage.cts.lib.TestUtils.getExternalStorageDir;
-import static android.scopedstorage.cts.lib.TestUtils.getFileMimeTypeFromDatabase;
 import static android.scopedstorage.cts.lib.TestUtils.getFileOwnerPackageFromDatabase;
 import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase;
-import static android.scopedstorage.cts.lib.TestUtils.getFileSizeFromDatabase;
 import static android.scopedstorage.cts.lib.TestUtils.getFileUri;
 import static android.scopedstorage.cts.lib.TestUtils.getImageContentUri;
 import static android.scopedstorage.cts.lib.TestUtils.getMoviesDir;
 import static android.scopedstorage.cts.lib.TestUtils.getMusicDir;
-import static android.scopedstorage.cts.lib.TestUtils.getNotificationsDir;
 import static android.scopedstorage.cts.lib.TestUtils.getPicturesDir;
 import static android.scopedstorage.cts.lib.TestUtils.getPodcastsDir;
-import static android.scopedstorage.cts.lib.TestUtils.getRingtonesDir;
-import static android.scopedstorage.cts.lib.TestUtils.grantPermission;
 import static android.scopedstorage.cts.lib.TestUtils.installApp;
 import static android.scopedstorage.cts.lib.TestUtils.installAppWithStoragePermissions;
 import static android.scopedstorage.cts.lib.TestUtils.listAs;
@@ -81,31 +62,18 @@
 import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
 import static android.scopedstorage.cts.lib.TestUtils.pollForManageExternalStorageAllowed;
 import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
-import static android.scopedstorage.cts.lib.TestUtils.queryFile;
-import static android.scopedstorage.cts.lib.TestUtils.queryFileExcludingPending;
 import static android.scopedstorage.cts.lib.TestUtils.queryImageFile;
 import static android.scopedstorage.cts.lib.TestUtils.queryVideoFile;
-import static android.scopedstorage.cts.lib.TestUtils.readExifMetadataFromTestApp;
-import static android.scopedstorage.cts.lib.TestUtils.revokePermission;
-import static android.scopedstorage.cts.lib.TestUtils.setAttrAs;
 import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
 import static android.scopedstorage.cts.lib.TestUtils.uninstallApp;
 import static android.scopedstorage.cts.lib.TestUtils.uninstallAppNoThrow;
-import static android.scopedstorage.cts.lib.TestUtils.updateDisplayNameWithMediaProvider;
 import static android.system.OsConstants.F_OK;
-import static android.system.OsConstants.O_APPEND;
-import static android.system.OsConstants.O_CREAT;
-import static android.system.OsConstants.O_EXCL;
-import static android.system.OsConstants.O_RDWR;
-import static android.system.OsConstants.O_TRUNC;
 import static android.system.OsConstants.R_OK;
-import static android.system.OsConstants.S_IRWXU;
 import static android.system.OsConstants.W_OK;
 
 import static androidx.test.InstrumentationRegistry.getContext;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -123,24 +91,16 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Environment;
-import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.platform.test.annotations.AppModeInstant;
 import android.provider.MediaStore;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.StructStat;
 import android.util.Log;
 
-import androidx.annotation.Nullable;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.cts.install.lib.TestApp;
 
-import com.google.common.io.Files;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -150,11 +110,8 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -220,551 +177,6 @@
     }
 
     /**
-     * Test that we enforce certain media types can only be created in certain directories.
-     */
-    @Test
-    public void testTypePathConformity() throws Exception {
-        final File dcimDir = getDcimDir();
-        final File documentsDir = getDocumentsDir();
-        final File downloadDir = getDownloadDir();
-        final File moviesDir = getMoviesDir();
-        final File musicDir = getMusicDir();
-        final File picturesDir = getPicturesDir();
-        // Only audio files can be created in Music
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(musicDir, NONMEDIA_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(musicDir, VIDEO_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(musicDir, IMAGE_FILE_NAME).createNewFile(); });
-        // Only video files can be created in Movies
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(moviesDir, NONMEDIA_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(moviesDir, AUDIO_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(moviesDir, IMAGE_FILE_NAME).createNewFile(); });
-        // Only image and video files can be created in DCIM
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(dcimDir, NONMEDIA_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(dcimDir, AUDIO_FILE_NAME).createNewFile(); });
-        // Only image and video files can be created in Pictures
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(picturesDir, NONMEDIA_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(picturesDir, AUDIO_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(picturesDir, PLAYLIST_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(dcimDir, SUBTITLE_FILE_NAME).createNewFile(); });
-
-        assertCanCreateFile(new File(getAlarmsDir(), AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(getAudiobooksDir(), AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(dcimDir, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(dcimDir, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(documentsDir, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(documentsDir, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(documentsDir, NONMEDIA_FILE_NAME));
-        assertCanCreateFile(new File(documentsDir, PLAYLIST_FILE_NAME));
-        assertCanCreateFile(new File(documentsDir, SUBTITLE_FILE_NAME));
-        assertCanCreateFile(new File(documentsDir, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(downloadDir, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(downloadDir, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(downloadDir, NONMEDIA_FILE_NAME));
-        assertCanCreateFile(new File(downloadDir, PLAYLIST_FILE_NAME));
-        assertCanCreateFile(new File(downloadDir, SUBTITLE_FILE_NAME));
-        assertCanCreateFile(new File(downloadDir, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(moviesDir, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(moviesDir, SUBTITLE_FILE_NAME));
-        assertCanCreateFile(new File(musicDir, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(musicDir, PLAYLIST_FILE_NAME));
-        assertCanCreateFile(new File(getNotificationsDir(), AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(picturesDir, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(picturesDir, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(getPodcastsDir(), AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(getRingtonesDir(), AUDIO_FILE_NAME));
-
-        // No file whatsoever can be created in the top level directory
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(getExternalStorageDir(), NONMEDIA_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(getExternalStorageDir(), AUDIO_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(getExternalStorageDir(), IMAGE_FILE_NAME).createNewFile(); });
-        assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(getExternalStorageDir(), VIDEO_FILE_NAME).createNewFile(); });
-    }
-
-    /**
-     * Test that we can create a file in app's external files directory,
-     * and that we can write and read to/from the file.
-     */
-    @Test
-    public void testCreateFileInAppExternalDir() throws Exception {
-        final File file = new File(getExternalFilesDir(), "text.txt");
-        try {
-            assertThat(file.createNewFile()).isTrue();
-            assertThat(file.delete()).isTrue();
-            // Ensure the file is properly deleted and can be created again
-            assertThat(file.createNewFile()).isTrue();
-
-            // Write to file
-            try (final FileOutputStream fos = new FileOutputStream(file)) {
-                fos.write(BYTES_DATA1);
-            }
-
-            // Read the same data from file
-            assertFileContent(file, BYTES_DATA1);
-        } finally {
-            file.delete();
-        }
-    }
-
-    /**
-     * Test that we can't create a file in another app's external files directory,
-     * and that we'll get the same error regardless of whether the app exists or not.
-     */
-    @Test
-    public void testCreateFileInOtherAppExternalDir() throws Exception {
-        // Creating a file in a non existent package dir should return ENOENT, as expected
-        final File nonexistentPackageFileDir = new File(
-                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
-        final File file1 = new File(nonexistentPackageFileDir, NONMEDIA_FILE_NAME);
-        assertThrows(
-                IOException.class, FILE_CREATION_ERROR_MESSAGE, () -> { file1.createNewFile(); });
-
-        // Creating a file in an existent package dir should give the same error string to avoid
-        // leaking installed app names, and we know the following directory exists because shell
-        // mkdirs it in test setup
-        final File shellPackageFileDir = new File(
-                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
-        final File file2 = new File(shellPackageFileDir, NONMEDIA_FILE_NAME);
-        assertThrows(
-                IOException.class, FILE_CREATION_ERROR_MESSAGE, () -> { file1.createNewFile(); });
-    }
-
-    /**
-     * Test that apps can't read/write files in another app's external files directory,
-     * and can do so in their own app's external file directory.
-     */
-    @Test
-    public void testReadWriteFilesInOtherAppExternalDir() throws Exception {
-        final File videoFile = new File(getExternalFilesDir(), VIDEO_FILE_NAME);
-
-        try {
-            // Create a file in app's external files directory
-            if (!videoFile.exists()) {
-                assertThat(videoFile.createNewFile()).isTrue();
-            }
-
-            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
-            installAppWithStoragePermissions(TEST_APP_A);
-
-            // TEST_APP_A should not be able to read/write to other app's external files directory.
-            assertThat(canOpenFileAs(TEST_APP_A, videoFile, false /* forWrite */)).isFalse();
-            assertThat(canOpenFileAs(TEST_APP_A, videoFile, true /* forWrite */)).isFalse();
-            // TEST_APP_A should not be able to delete files in other app's external files
-            // directory.
-            assertThat(deleteFileAs(TEST_APP_A, videoFile.getPath())).isFalse();
-
-            // Apps should have read/write access in their own app's external files directory.
-            assertThat(canOpen(videoFile, false /* forWrite */)).isTrue();
-            assertThat(canOpen(videoFile, true /* forWrite */)).isTrue();
-            // Apps should be able to delete files in their own app's external files directory.
-            assertThat(videoFile.delete()).isTrue();
-        } finally {
-            videoFile.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that we can contribute media without any permissions.
-     */
-    @Test
-    public void testContributeMediaFile() throws Exception {
-        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
-
-        try {
-            assertThat(imageFile.createNewFile()).isTrue();
-
-            // Ensure that the file was successfully added to the MediaProvider database
-            assertThat(getFileOwnerPackageFromDatabase(imageFile)).isEqualTo(THIS_PACKAGE_NAME);
-
-            // Try to write random data to the file
-            try (final FileOutputStream fos = new FileOutputStream(imageFile)) {
-                fos.write(BYTES_DATA1);
-                fos.write(BYTES_DATA2);
-            }
-
-            final byte[] expected = (STR_DATA1 + STR_DATA2).getBytes();
-            assertFileContent(imageFile, expected);
-
-            // Closing the file after writing will not trigger a MediaScan. Call scanFile to update
-            // file's entry in MediaProvider's database.
-            assertThat(MediaStore.scanFile(getContentResolver(), imageFile)).isNotNull();
-
-            // Ensure that the scan was completed and the file's size was updated.
-            assertThat(getFileSizeFromDatabase(imageFile)).isEqualTo(
-                    BYTES_DATA1.length + BYTES_DATA2.length);
-        } finally {
-            imageFile.delete();
-        }
-        // Ensure that delete makes a call to MediaProvider to remove the file from its database.
-        assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(-1);
-    }
-
-    @Test
-    public void testCreateAndDeleteEmptyDir() throws Exception {
-        final File externalFilesDir = getExternalFilesDir();
-        // Remove directory in order to create it again
-        externalFilesDir.delete();
-
-        // Can create own external files dir
-        assertThat(externalFilesDir.mkdir()).isTrue();
-
-        final File dir1 = new File(externalFilesDir, "random_dir");
-        // Can create dirs inside it
-        assertThat(dir1.mkdir()).isTrue();
-
-        final File dir2 = new File(dir1, "random_dir_inside_random_dir");
-        // And create a dir inside the new dir
-        assertThat(dir2.mkdir()).isTrue();
-
-        // And can delete them all
-        assertThat(dir2.delete()).isTrue();
-        assertThat(dir1.delete()).isTrue();
-        assertThat(externalFilesDir.delete()).isTrue();
-
-        // Can't create external dir for other apps
-        final File nonexistentPackageFileDir = new File(
-                externalFilesDir.getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
-        final File shellPackageFileDir = new File(
-                externalFilesDir.getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
-
-        assertThat(nonexistentPackageFileDir.mkdir()).isFalse();
-        assertThat(shellPackageFileDir.mkdir()).isFalse();
-    }
-
-    @Test
-    public void testCantAccessOtherAppsContents() throws Exception {
-        final File mediaFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
-        final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        try {
-            installApp(TEST_APP_A);
-
-            assertThat(createFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
-            assertThat(createFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
-
-            // We can still see that the files exist
-            assertThat(mediaFile.exists()).isTrue();
-            assertThat(nonMediaFile.exists()).isTrue();
-
-            // But we can't access their content
-            assertThat(canOpen(mediaFile, /* forWrite */ false)).isFalse();
-            assertThat(canOpen(nonMediaFile, /* forWrite */ true)).isFalse();
-            assertThat(canOpen(mediaFile, /* forWrite */ false)).isFalse();
-            assertThat(canOpen(nonMediaFile, /* forWrite */ true)).isFalse();
-        } finally {
-            deleteFileAsNoThrow(TEST_APP_A, nonMediaFile.getPath());
-            deleteFileAsNoThrow(TEST_APP_A, mediaFile.getPath());
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    @Test
-    public void testCantDeleteOtherAppsContents() throws Exception {
-        final File dirInDownload = new File(getDownloadDir(), TEST_DIRECTORY_NAME);
-        final File mediaFile = new File(dirInDownload, IMAGE_FILE_NAME);
-        final File nonMediaFile = new File(dirInDownload, NONMEDIA_FILE_NAME);
-        try {
-            installApp(TEST_APP_A);
-            assertThat(dirInDownload.mkdir()).isTrue();
-            // Have another app create a media file in the directory
-            assertThat(createFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
-
-            // Can't delete the directory since it contains another app's content
-            assertThat(dirInDownload.delete()).isFalse();
-            // Can't delete another app's content
-            assertThat(deleteRecursively(dirInDownload)).isFalse();
-
-            // Have another app create a non-media file in the directory
-            assertThat(createFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
-
-            // Can't delete the directory since it contains another app's content
-            assertThat(dirInDownload.delete()).isFalse();
-            // Can't delete another app's content
-            assertThat(deleteRecursively(dirInDownload)).isFalse();
-
-            // Delete only the media file and keep the non-media file
-            assertThat(deleteFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
-            // Directory now has only the non-media file contributed by another app, so we still
-            // can't delete it nor its content
-            assertThat(dirInDownload.delete()).isFalse();
-            assertThat(deleteRecursively(dirInDownload)).isFalse();
-
-            // Delete the last file belonging to another app
-            assertThat(deleteFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
-            // Create our own file
-            assertThat(nonMediaFile.createNewFile()).isTrue();
-
-            // Now that the directory only has content that was contributed by us, we can delete it
-            assertThat(deleteRecursively(dirInDownload)).isTrue();
-        } finally {
-            deleteFileAsNoThrow(TEST_APP_A, nonMediaFile.getPath());
-            deleteFileAsNoThrow(TEST_APP_A, mediaFile.getPath());
-            // At this point, we're not sure who created this file, so we'll have both apps
-            // deleting it
-            mediaFile.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-            dirInDownload.delete();
-        }
-    }
-
-    /**
-     * Test that deleting uri corresponding to a file which was already deleted via filePath
-     * doesn't result in a security exception.
-     */
-    @Test
-    public void testDeleteAlreadyUnlinkedFile() throws Exception {
-        final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        try {
-            assertTrue(nonMediaFile.createNewFile());
-            final Uri uri = MediaStore.scanFile(getContentResolver(), nonMediaFile);
-            assertNotNull(uri);
-
-            // Delete the file via filePath
-            assertTrue(nonMediaFile.delete());
-
-            // If we delete nonMediaFile with ContentResolver#delete, it shouldn't result in a
-            // security exception.
-            assertThat(getContentResolver().delete(uri, Bundle.EMPTY)).isEqualTo(0);
-        } finally {
-            nonMediaFile.delete();
-        }
-    }
-
-    /**
-     * This test relies on the fact that {@link File#list} uses opendir internally, and that it
-     * returns {@code null} if opendir fails.
-     */
-    @Test
-    public void testOpendirRestrictions() throws Exception {
-        // Opening a non existent package directory should fail, as expected
-        final File nonexistentPackageFileDir = new File(
-                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
-        assertThat(nonexistentPackageFileDir.list()).isNull();
-
-        // Opening another package's external directory should fail as well, even if it exists
-        final File shellPackageFileDir = new File(
-                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
-        assertThat(shellPackageFileDir.list()).isNull();
-
-        // We can open our own external files directory
-        final String[] filesList = getExternalFilesDir().list();
-        assertThat(filesList).isNotNull();
-
-        // We can open any public directory in external storage
-        assertThat(getDcimDir().list()).isNotNull();
-        assertThat(getDownloadDir().list()).isNotNull();
-        assertThat(getMoviesDir().list()).isNotNull();
-        assertThat(getMusicDir().list()).isNotNull();
-
-        // We can open the root directory of external storage
-        final String[] topLevelDirs = getExternalStorageDir().list();
-        assertThat(topLevelDirs).isNotNull();
-        // TODO(b/145287327): This check fails on a device with no visible files.
-        // This can be fixed if we display default directories.
-        // assertThat(topLevelDirs).isNotEmpty();
-    }
-
-    @Test
-    public void testLowLevelFileIO() throws Exception {
-        String filePath = new File(getDownloadDir(), NONMEDIA_FILE_NAME).toString();
-        try {
-            int createFlags = O_CREAT | O_RDWR;
-            int createExclFlags = createFlags | O_EXCL;
-
-            FileDescriptor fd = Os.open(filePath, createExclFlags, S_IRWXU);
-            Os.close(fd);
-            assertThrows(
-                    ErrnoException.class, () -> { Os.open(filePath, createExclFlags, S_IRWXU); });
-
-            fd = Os.open(filePath, createFlags, S_IRWXU);
-            try {
-                assertThat(Os.write(fd, ByteBuffer.wrap(BYTES_DATA1))).isEqualTo(BYTES_DATA1.length);
-                assertFileContent(fd, BYTES_DATA1);
-            } finally {
-                Os.close(fd);
-            }
-            // should just append the data
-            fd = Os.open(filePath, createFlags | O_APPEND, S_IRWXU);
-            try {
-                assertThat(Os.write(fd, ByteBuffer.wrap(BYTES_DATA2))).isEqualTo(BYTES_DATA2.length);
-                final byte[] expected = (STR_DATA1 + STR_DATA2).getBytes();
-                assertFileContent(fd, expected);
-            } finally {
-                Os.close(fd);
-            }
-            // should overwrite everything
-            fd = Os.open(filePath, createFlags | O_TRUNC, S_IRWXU);
-            try {
-                final byte[] otherData = "this is different data".getBytes();
-                assertThat(Os.write(fd, ByteBuffer.wrap(otherData))).isEqualTo(otherData.length);
-                assertFileContent(fd, otherData);
-            } finally {
-                Os.close(fd);
-            }
-        } finally {
-            new File(filePath).delete();
-        }
-    }
-
-    /**
-     * Test that media files from other packages are only visible to apps with storage permission.
-     */
-    @Test
-    public void testListDirectoriesWithMediaFiles() throws Exception {
-        final File dcimDir = getDcimDir();
-        final File dir = new File(dcimDir, TEST_DIRECTORY_NAME);
-        final File videoFile = new File(dir, VIDEO_FILE_NAME);
-        final String videoFileName = videoFile.getName();
-        try {
-            if (!dir.exists()) {
-                assertThat(dir.mkdir()).isTrue();
-            }
-
-            // Install TEST_APP_A and create media file in the new directory.
-            installApp(TEST_APP_A);
-            assertThat(createFileAs(TEST_APP_A, videoFile.getPath())).isTrue();
-            // TEST_APP_A should see TEST_DIRECTORY in DCIM and new file in TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_A, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
-            assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(videoFileName);
-
-            // Install TEST_APP_B with storage permission.
-            installAppWithStoragePermissions(TEST_APP_B);
-            // TEST_APP_B with storage permission should see TEST_DIRECTORY in DCIM and new file
-            // in TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
-            assertThat(listAs(TEST_APP_B, dir.getPath())).containsExactly(videoFileName);
-
-            // Revoke storage permission for TEST_APP_B
-            revokePermission(
-                    TEST_APP_B.getPackageName(), Manifest.permission.READ_EXTERNAL_STORAGE);
-            // TEST_APP_B without storage permission should see TEST_DIRECTORY in DCIM and should
-            // not see new file in new TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
-            assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(videoFileName);
-        } finally {
-            uninstallAppNoThrow(TEST_APP_B);
-            deleteFileAsNoThrow(TEST_APP_A, videoFile.getPath());
-            dir.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that app can't see non-media files created by other packages
-     */
-    @Test
-    public void testListDirectoriesWithNonMediaFiles() throws Exception {
-        final File downloadDir = getDownloadDir();
-        final File dir = new File(downloadDir, TEST_DIRECTORY_NAME);
-        final File pdfFile = new File(dir, NONMEDIA_FILE_NAME);
-        final String pdfFileName = pdfFile.getName();
-        try {
-            if (!dir.exists()) {
-                assertThat(dir.mkdir()).isTrue();
-            }
-
-            // Install TEST_APP_A and create non media file in the new directory.
-            installApp(TEST_APP_A);
-            assertThat(createFileAs(TEST_APP_A, pdfFile.getPath())).isTrue();
-
-            // TEST_APP_A should see TEST_DIRECTORY in downloadDir and new non media file in
-            // TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_A, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
-            assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(pdfFileName);
-
-            // Install TEST_APP_B with storage permission.
-            installAppWithStoragePermissions(TEST_APP_B);
-            // TEST_APP_B with storage permission should see TEST_DIRECTORY in downloadDir
-            // and should not see new non media file in TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_B, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
-            assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(pdfFileName);
-        } finally {
-            uninstallAppNoThrow(TEST_APP_B);
-            deleteFileAsNoThrow(TEST_APP_A, pdfFile.getPath());
-            dir.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that app can only see its directory in Android/data.
-     */
-    @Test
-    public void testListFilesFromExternalFilesDirectory() throws Exception {
-        final String packageName = THIS_PACKAGE_NAME;
-        final File nonmediaFile = new File(getExternalFilesDir(), NONMEDIA_FILE_NAME);
-
-        try {
-            // Create a file in app's external files directory
-            if (!nonmediaFile.exists()) {
-                assertThat(nonmediaFile.createNewFile()).isTrue();
-            }
-            // App should see its directory and directories of shared packages. App should see all
-            // files and directories in its external directory.
-            assertDirectoryContains(nonmediaFile.getParentFile(), nonmediaFile);
-
-            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
-            // TEST_APP_A should not see other app's external files directory.
-            installAppWithStoragePermissions(TEST_APP_A);
-
-            assertThrows(IOException.class,
-                    () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
-            assertThrows(IOException.class,
-                    () -> listAs(TEST_APP_A, getExternalFilesDir().getPath()));
-        } finally {
-            nonmediaFile.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that app can see files and directories in Android/media.
-     */
-    @Test
-    public void testListFilesFromExternalMediaDirectory() throws Exception {
-        final File videoFile = new File(getExternalMediaDir(), VIDEO_FILE_NAME);
-
-        try {
-            // Create a file in app's external media directory
-            if (!videoFile.exists()) {
-                assertThat(videoFile.createNewFile()).isTrue();
-            }
-
-            // App should see its directory and other app's external media directories with media
-            // files.
-            assertDirectoryContains(videoFile.getParentFile(), videoFile);
-
-            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
-            // TEST_APP_A with storage permission should see other app's external media directory.
-            installAppWithStoragePermissions(TEST_APP_A);
-            // Apps with READ_EXTERNAL_STORAGE can list files in other app's external media
-            // directory.
-            assertThat(listAs(TEST_APP_A, getAndroidMediaDir().getPath()))
-                    .contains(THIS_PACKAGE_NAME);
-            assertThat(listAs(TEST_APP_A, getExternalMediaDir().getPath()))
-                    .containsExactly(videoFile.getName());
-        } finally {
-            videoFile.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
      * Test that readdir lists unsupported file types in default directories.
      */
     @Test
@@ -795,857 +207,7 @@
         }
     }
 
-    @Test
-    public void testMetaDataRedaction() throws Exception {
-        File jpgFile = new File(getPicturesDir(), "img_metadata.jpg");
-        try {
-            if (jpgFile.exists()) {
-                assertThat(jpgFile.delete()).isTrue();
-            }
-
-            HashMap<String, String> originalExif =
-                    getExifMetadataFromRawResource(R.raw.img_with_metadata);
-
-            try (InputStream in =
-                            getContext().getResources().openRawResource(R.raw.img_with_metadata);
-                    OutputStream out = new FileOutputStream(jpgFile)) {
-                // Dump the image we have to external storage
-                FileUtils.copy(in, out);
-
-                HashMap<String, String> exif = getExifMetadata(jpgFile);
-                assertExifMetadataMatch(exif, originalExif);
-
-                installAppWithStoragePermissions(TEST_APP_A);
-                HashMap<String, String> exifFromTestApp =
-                        readExifMetadataFromTestApp(TEST_APP_A, jpgFile.getPath());
-                // Other apps shouldn't have access to the same metadata without explicit permission
-                assertExifMetadataMismatch(exifFromTestApp, originalExif);
-
-                // TODO(b/146346138): Test that if we give TEST_APP_A write URI permission,
-                //  it would be able to access the metadata.
-            } // Intentionally keep the original streams open during the test so bytes are more
-            // likely to be in the VFS cache from both file opens
-        } finally {
-            jpgFile.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    @Test
-    public void testOpenFilePathFirstWriteContentResolver() throws Exception {
-        String displayName = "open_file_path_write_content_resolver.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            ParcelFileDescriptor readPfd =
-                    ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
-            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
-
-            assertRWR(readPfd, writePfd);
-            assertUpperFsFd(writePfd); // With cache
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testOpenContentResolverFirstWriteContentResolver() throws Exception {
-        String displayName = "open_content_resolver_write_content_resolver.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
-            ParcelFileDescriptor readPfd =
-                    ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
-
-            assertRWR(readPfd, writePfd);
-            assertLowerFsFd(writePfd);
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testOpenFilePathFirstWriteFilePath() throws Exception {
-        String displayName = "open_file_path_write_file_path.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            ParcelFileDescriptor writePfd =
-                    ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
-            ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
-
-            assertRWR(readPfd, writePfd);
-            assertUpperFsFd(readPfd); // With cache
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testOpenContentResolverFirstWriteFilePath() throws Exception {
-        String displayName = "open_content_resolver_write_file_path.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
-            ParcelFileDescriptor writePfd =
-                    ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
-
-            assertRWR(readPfd, writePfd);
-            assertLowerFsFd(readPfd);
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testOpenContentResolverWriteOnly() throws Exception {
-        String displayName = "open_content_resolver_write_only.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            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
-            assertLowerFsFd(writePfd);
-            assertLowerFsFd(readPfd);
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testOpenContentResolverDup() throws Exception {
-        String displayName = "open_content_resolver_dup.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            file.delete();
-            assertThat(file.createNewFile()).isTrue();
-
-            // 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, ParcelFileDescriptor.MODE_READ_WRITE)) {
-                    writePfd.close();
-
-                    assertRWR(readPfd, writePfdDup);
-                    assertLowerFsFd(writePfdDup);
-                }
-            }
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testOpenContentResolverClose() throws Exception {
-        String displayName = "open_content_resolver_close.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            byte[] readBuffer = new byte[10];
-            byte[] writeBuffer = new byte[10];
-            Arrays.fill(writeBuffer, (byte) 1);
-
-            assertThat(file.createNewFile()).isTrue();
-
-            // Lower fs open and write
-            ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
-            Os.pwrite(writePfd.getFileDescriptor(), writeBuffer, 0, 10, 0);
-
-            // Close so upper fs open will not use direct_io
-            writePfd.close();
-
-            // Upper fs open and read without direct_io
-            ParcelFileDescriptor readPfd =
-                    ParcelFileDescriptor.open(file, ParcelFileDescriptor.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);
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testContentResolverDelete() throws Exception {
-        String displayName = "content_resolver_delete.jpg";
-        File file = new File(getDcimDir(), displayName);
-
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            deleteWithMediaProvider(file);
-
-            assertThat(file.exists()).isFalse();
-            assertThat(file.createNewFile()).isTrue();
-        } finally {
-            file.delete();
-        }
-    }
-
-    @Test
-    public void testContentResolverUpdate() throws Exception {
-        String oldDisplayName = "content_resolver_update_old.jpg";
-        String newDisplayName = "content_resolver_update_new.jpg";
-        File oldFile = new File(getDcimDir(), oldDisplayName);
-        File newFile = new File(getDcimDir(), newDisplayName);
-
-        try {
-            assertThat(oldFile.createNewFile()).isTrue();
-            // Publish the pending oldFile before updating with MediaProvider. Not publishing the
-            // file will make MP consider pending from FUSE as explicit IS_PENDING
-            final Uri uri = MediaStore.scanFile(getContentResolver(), oldFile);
-            assertNotNull(uri);
-
-            updateDisplayNameWithMediaProvider(uri,
-                    Environment.DIRECTORY_DCIM, oldDisplayName, newDisplayName);
-
-            assertThat(oldFile.exists()).isFalse();
-            assertThat(oldFile.createNewFile()).isTrue();
-            assertThat(newFile.exists()).isTrue();
-            assertThat(newFile.createNewFile()).isFalse();
-        } finally {
-            oldFile.delete();
-            newFile.delete();
-        }
-    }
-
-    @Test
-    public void testCreateLowerCaseDeleteUpperCase() throws Exception {
-        File upperCase = new File(getDownloadDir(), "CREATE_LOWER_DELETE_UPPER");
-        File lowerCase = new File(getDownloadDir(), "create_lower_delete_upper");
-
-        createDeleteCreate(lowerCase, upperCase);
-    }
-
-    @Test
-    public void testCreateUpperCaseDeleteLowerCase() throws Exception {
-        File upperCase = new File(getDownloadDir(), "CREATE_UPPER_DELETE_LOWER");
-        File lowerCase = new File(getDownloadDir(), "create_upper_delete_lower");
-
-        createDeleteCreate(upperCase, lowerCase);
-    }
-
-    @Test
-    public void testCreateMixedCaseDeleteDifferentMixedCase() throws Exception {
-        File mixedCase1 = new File(getDownloadDir(), "CrEaTe_MiXeD_dElEtE_mIxEd");
-        File mixedCase2 = new File(getDownloadDir(), "cReAtE_mIxEd_DeLeTe_MiXeD");
-
-        createDeleteCreate(mixedCase1, mixedCase2);
-    }
-
-    @Test
-    public void testAndroidDataObbDoesNotForgetMount() throws Exception {
-        File dataDir = getContext().getExternalFilesDir(null);
-        File upperCaseDataDir = new File(dataDir.getPath().replace("Android/data", "ANDROID/DATA"));
-
-        File obbDir = getContext().getObbDir();
-        File upperCaseObbDir = new File(obbDir.getPath().replace("Android/obb", "ANDROID/OBB"));
-
-
-        StructStat beforeDataStruct = Os.stat(dataDir.getPath());
-        StructStat beforeObbStruct = Os.stat(obbDir.getPath());
-
-        assertThat(dataDir.exists()).isTrue();
-        assertThat(upperCaseDataDir.exists()).isTrue();
-        assertThat(obbDir.exists()).isTrue();
-        assertThat(upperCaseObbDir.exists()).isTrue();
-
-        StructStat afterDataStruct = Os.stat(upperCaseDataDir.getPath());
-        StructStat afterObbStruct = Os.stat(upperCaseObbDir.getPath());
-
-        assertThat(beforeDataStruct.st_dev).isEqualTo(afterDataStruct.st_dev);
-        assertThat(beforeObbStruct.st_dev).isEqualTo(afterObbStruct.st_dev);
-    }
-
-    @Test
-    public void testCacheConsistencyForCaseInsensitivity() throws Exception {
-        File upperCaseFile = new File(getDownloadDir(), "CACHE_CONSISTENCY_FOR_CASE_INSENSITIVITY");
-        File lowerCaseFile = new File(getDownloadDir(), "cache_consistency_for_case_insensitivity");
-
-        try {
-            ParcelFileDescriptor upperCasePfd =
-                    ParcelFileDescriptor.open(upperCaseFile,
-                            ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
-            ParcelFileDescriptor lowerCasePfd =
-                    ParcelFileDescriptor.open(lowerCaseFile,
-                            ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
-
-            assertRWR(upperCasePfd, lowerCasePfd);
-            assertRWR(lowerCasePfd, upperCasePfd);
-        } finally {
-            upperCaseFile.delete();
-            lowerCaseFile.delete();
-        }
-    }
-
-    private void createDeleteCreate(File create, File delete) throws Exception {
-        try {
-            assertThat(create.createNewFile()).isTrue();
-            Thread.sleep(5);
-
-            assertThat(delete.delete()).isTrue();
-            Thread.sleep(5);
-
-            assertThat(create.createNewFile()).isTrue();
-            Thread.sleep(5);
-        } finally {
-            create.delete();
-            delete.delete();
-        }
-    }
-
-    @Test
-    public void testReadStorageInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "read_storage.jpg"),
-                Manifest.permission.READ_EXTERNAL_STORAGE,
-                AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, /* forWrite */ false);
-    }
-
-    @Test
-    public void testWriteStorageInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C_LEGACY, new File(getDcimDir(), "write_storage.jpg"),
-                Manifest.permission.WRITE_EXTERNAL_STORAGE,
-                AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE, /* forWrite */ true);
-    }
-
-    @Test
-    public void testManageStorageInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(getDownloadDir(), "manage_storage.pdf"),
-                /* permission */ null, OPSTR_MANAGE_EXTERNAL_STORAGE, /* forWrite */ true);
-    }
-
-    @Test
-    public void testWriteImagesInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_images.jpg"),
-                /* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES, /* forWrite */ true);
-    }
-
-    @Test
-    public void testWriteVideoInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_video.mp4"),
-                /* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO, /* forWrite */ true);
-    }
-
-    @Test
-    public void testAccessMediaLocationInvalidation() throws Exception {
-        File imgFile = new File(getDcimDir(), "access_media_location.jpg");
-
-        try {
-            // Setup image with sensitive data on external storage
-            HashMap<String, String> originalExif =
-                    getExifMetadataFromRawResource(R.raw.img_with_metadata);
-            try (InputStream in =
-                            getContext().getResources().openRawResource(R.raw.img_with_metadata);
-                    OutputStream out = new FileOutputStream(imgFile)) {
-                // Dump the image we have to external storage
-                FileUtils.copy(in, out);
-            }
-            HashMap<String, String> exif = getExifMetadata(imgFile);
-            assertExifMetadataMatch(exif, originalExif);
-
-            // Install test app
-            installAppWithStoragePermissions(TEST_APP_C);
-
-            // Grant A_M_L and verify access to sensitive data
-            grantPermission(TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
-            HashMap<String, String> exifFromTestApp =
-                    readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
-            assertExifMetadataMatch(exifFromTestApp, originalExif);
-
-            // Revoke A_M_L and verify sensitive data redaction
-            revokePermission(
-                    TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
-            exifFromTestApp = readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
-            assertExifMetadataMismatch(exifFromTestApp, originalExif);
-
-            // Re-grant A_M_L and verify access to sensitive data
-            grantPermission(TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
-            exifFromTestApp = readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
-            assertExifMetadataMatch(exifFromTestApp, originalExif);
-        } finally {
-            imgFile.delete();
-            uninstallAppNoThrow(TEST_APP_C);
-        }
-    }
-
-    @Test
-    public void testAppUpdateInvalidation() throws Exception {
-        File file = new File(getDcimDir(), "app_update.jpg");
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            // Install legacy
-            installAppWithStoragePermissions(TEST_APP_C_LEGACY);
-            grantPermission(TEST_APP_C_LEGACY.getPackageName(),
-                    Manifest.permission.WRITE_EXTERNAL_STORAGE); // Grants write access for legacy
-            // Legacy app can read and write media files contributed by others
-            assertThat(canOpenFileAs(TEST_APP_C_LEGACY, file, /* forWrite */ false)).isTrue();
-            assertThat(canOpenFileAs(TEST_APP_C_LEGACY, file, /* forWrite */ true)).isTrue();
-
-            // Update to non-legacy
-            installAppWithStoragePermissions(TEST_APP_C);
-            grantPermission(TEST_APP_C_LEGACY.getPackageName(),
-                    Manifest.permission.WRITE_EXTERNAL_STORAGE); // No effect for non-legacy
-            // Non-legacy app can read media files contributed by others
-            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isTrue();
-            // But cannot write
-            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ true)).isFalse();
-        } finally {
-            file.delete();
-            uninstallAppNoThrow(TEST_APP_C);
-        }
-    }
-
-    @Test
-    public void testAppReinstallInvalidation() throws Exception {
-        File file = new File(getDcimDir(), "app_reinstall.jpg");
-
-        try {
-            assertThat(file.createNewFile()).isTrue();
-
-            // Install
-            installAppWithStoragePermissions(TEST_APP_C);
-            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isTrue();
-
-            // Re-install
-            uninstallAppNoThrow(TEST_APP_C);
-            installApp(TEST_APP_C);
-            assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isFalse();
-        } finally {
-            file.delete();
-            uninstallAppNoThrow(TEST_APP_C);
-        }
-    }
-
-    private void testAppOpInvalidation(TestApp app, File file, @Nullable String permission,
-            String opstr, boolean forWrite) throws Exception {
-        try {
-            installApp(app);
-            assertThat(file.createNewFile()).isTrue();
-            assertAppOpInvalidation(app, file, permission, opstr, forWrite);
-        } finally {
-            file.delete();
-            uninstallApp(app);
-        }
-    }
-
-    /** If {@code permission} is null, appops are flipped, otherwise permissions are flipped */
-    private void assertAppOpInvalidation(TestApp app, File file, @Nullable String permission,
-            String opstr, boolean forWrite) throws Exception {
-        String packageName = app.getPackageName();
-        int uid = getContext().getPackageManager().getPackageUid(packageName, 0);
-
-        // Deny
-        if (permission != null) {
-            revokePermission(packageName, permission);
-        } else {
-            denyAppOpsToUid(uid, opstr);
-        }
-        assertThat(canOpenFileAs(app, file, forWrite)).isFalse();
-
-        // Grant
-        if (permission != null) {
-            grantPermission(packageName, permission);
-        } else {
-            allowAppOpsToUid(uid, opstr);
-        }
-        assertThat(canOpenFileAs(app, file, forWrite)).isTrue();
-
-        // Deny
-        if (permission != null) {
-            revokePermission(packageName, permission);
-        } else {
-            denyAppOpsToUid(uid, opstr);
-        }
-        assertThat(canOpenFileAs(app, file, forWrite)).isFalse();
-    }
-
-    @Test
-    public void testSystemGalleryAppHasFullAccessToImages() throws Exception {
-        final File otherAppImageFile = new File(getDcimDir(), "other_" + IMAGE_FILE_NAME);
-        final File topLevelImageFile = new File(getExternalStorageDir(), IMAGE_FILE_NAME);
-        final File imageInAnObviouslyWrongPlace = new File(getMusicDir(), IMAGE_FILE_NAME);
-
-        try {
-            installApp(TEST_APP_A);
-            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-
-            // Have another app create an image file
-            assertThat(createFileAs(TEST_APP_A, otherAppImageFile.getPath())).isTrue();
-            assertThat(otherAppImageFile.exists()).isTrue();
-
-            // Assert we can write to the file
-            try (final FileOutputStream fos = new FileOutputStream(otherAppImageFile)) {
-                fos.write(BYTES_DATA1);
-            }
-
-            // Assert we can read from the file
-            assertFileContent(otherAppImageFile, BYTES_DATA1);
-
-            // Assert we can delete the file
-            assertThat(otherAppImageFile.delete()).isTrue();
-            assertThat(otherAppImageFile.exists()).isFalse();
-
-            // Can create an image anywhere
-            assertCanCreateFile(topLevelImageFile);
-            assertCanCreateFile(imageInAnObviouslyWrongPlace);
-
-            // Put the file back in its place and let TEST_APP_A delete it
-            assertThat(otherAppImageFile.createNewFile()).isTrue();
-        } finally {
-            deleteFileAsNoThrow(TEST_APP_A, otherAppImageFile.getAbsolutePath());
-            otherAppImageFile.delete();
-            uninstallApp(TEST_APP_A);
-            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-        }
-    }
-
-    @Test
-    public void testSystemGalleryAppHasNoFullAccessToAudio() throws Exception {
-        final File otherAppAudioFile = new File(getMusicDir(), "other_" + AUDIO_FILE_NAME);
-        final File topLevelAudioFile = new File(getExternalStorageDir(), AUDIO_FILE_NAME);
-        final File audioInAnObviouslyWrongPlace = new File(getPicturesDir(), AUDIO_FILE_NAME);
-
-        try {
-            installApp(TEST_APP_A);
-            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-
-            // Have another app create an audio file
-            assertThat(createFileAs(TEST_APP_A, otherAppAudioFile.getPath())).isTrue();
-            assertThat(otherAppAudioFile.exists()).isTrue();
-
-            // Assert we can't access the file
-            assertThat(canOpen(otherAppAudioFile, /* forWrite */ false)).isFalse();
-            assertThat(canOpen(otherAppAudioFile, /* forWrite */ true)).isFalse();
-
-            // Assert we can't delete the file
-            assertThat(otherAppAudioFile.delete()).isFalse();
-
-            // Can't create an audio file where it doesn't belong
-            assertThrows(IOException.class, "Operation not permitted",
-                    () -> { topLevelAudioFile.createNewFile(); });
-            assertThrows(IOException.class, "Operation not permitted",
-                    () -> { audioInAnObviouslyWrongPlace.createNewFile(); });
-        } finally {
-            deleteFileAs(TEST_APP_A, otherAppAudioFile.getPath());
-            uninstallApp(TEST_APP_A);
-            topLevelAudioFile.delete();
-            audioInAnObviouslyWrongPlace.delete();
-            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-        }
-    }
-
-    @Test
-    public void testSystemGalleryCanRenameImagesAndVideos() throws Exception {
-        final File otherAppVideoFile = new File(getDcimDir(), "other_" + VIDEO_FILE_NAME);
-        final File imageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
-        final File videoFile = new File(getPicturesDir(), VIDEO_FILE_NAME);
-        final File topLevelVideoFile = new File(getExternalStorageDir(), VIDEO_FILE_NAME);
-        final File musicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
-        try {
-            installApp(TEST_APP_A);
-            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-
-            // Have another app create a video file
-            assertThat(createFileAs(TEST_APP_A, otherAppVideoFile.getPath())).isTrue();
-            assertThat(otherAppVideoFile.exists()).isTrue();
-
-            // Write some data to the file
-            try (final FileOutputStream fos = new FileOutputStream(otherAppVideoFile)) {
-                fos.write(BYTES_DATA1);
-            }
-            assertFileContent(otherAppVideoFile, BYTES_DATA1);
-
-            // Assert we can rename the file and ensure the file has the same content
-            assertCanRenameFile(otherAppVideoFile, videoFile);
-            assertFileContent(videoFile, BYTES_DATA1);
-            // We can even move it to the top level directory
-            assertCanRenameFile(videoFile, topLevelVideoFile);
-            assertFileContent(topLevelVideoFile, BYTES_DATA1);
-            // And we can even convert it into an image file, because why not?
-            assertCanRenameFile(topLevelVideoFile, imageFile);
-            assertFileContent(imageFile, BYTES_DATA1);
-
-            // We can convert it to a music file, but we won't have access to music file after
-            // renaming.
-            assertThat(imageFile.renameTo(musicFile)).isTrue();
-            assertThat(getFileRowIdFromDatabase(musicFile)).isEqualTo(-1);
-        } finally {
-            deleteFileAsNoThrow(TEST_APP_A, otherAppVideoFile.getAbsolutePath());
-            uninstallApp(TEST_APP_A);
-            imageFile.delete();
-            videoFile.delete();
-            topLevelVideoFile.delete();
-            executeShellCommand("rm  " + musicFile.getAbsolutePath());
-            MediaStore.scanFile(getContentResolver(), musicFile);
-            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-        }
-    }
-
-    /**
-     * Test that basic file path restrictions are enforced on file rename.
-     */
-    @Test
-    public void testRenameFile() throws Exception {
-        final File downloadDir = getDownloadDir();
-        final File nonMediaDir = new File(downloadDir, TEST_DIRECTORY_NAME);
-        final File pdfFile1 = new File(downloadDir, NONMEDIA_FILE_NAME);
-        final File pdfFile2 = new File(nonMediaDir, NONMEDIA_FILE_NAME);
-        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
-        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
-        final File videoFile3 = new File(downloadDir, VIDEO_FILE_NAME);
-
-        try {
-            // Renaming non media file to media directory is not allowed.
-            assertThat(pdfFile1.createNewFile()).isTrue();
-            assertCantRenameFile(pdfFile1, new File(getDcimDir(), NONMEDIA_FILE_NAME));
-            assertCantRenameFile(pdfFile1, new File(getMusicDir(), NONMEDIA_FILE_NAME));
-            assertCantRenameFile(pdfFile1, new File(getMoviesDir(), NONMEDIA_FILE_NAME));
-
-            // Renaming non media files to non media directories is allowed.
-            if (!nonMediaDir.exists()) {
-                assertThat(nonMediaDir.mkdirs()).isTrue();
-            }
-            // App can rename pdfFile to non media directory.
-            assertCanRenameFile(pdfFile1, pdfFile2);
-
-            assertThat(videoFile1.createNewFile()).isTrue();
-            // App can rename video file to Movies directory
-            assertCanRenameFile(videoFile1, videoFile2);
-            // App can rename video file to Download directory
-            assertCanRenameFile(videoFile2, videoFile3);
-        } finally {
-            pdfFile1.delete();
-            pdfFile2.delete();
-            videoFile1.delete();
-            videoFile2.delete();
-            videoFile3.delete();
-            nonMediaDir.delete();
-        }
-    }
-
-    /**
-     * Test that renaming file to different mime type is allowed.
-     */
-    @Test
-    public void testRenameFileType() throws Exception {
-        final File pdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        final File videoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
-        try {
-            assertThat(pdfFile.createNewFile()).isTrue();
-            assertThat(videoFile.exists()).isFalse();
-            // Moving pdfFile to DCIM directory is not allowed.
-            assertCantRenameFile(pdfFile, new File(getDcimDir(), NONMEDIA_FILE_NAME));
-            // However, moving pdfFile to DCIM directory with changing the mime type to video is
-            // allowed.
-            assertCanRenameFile(pdfFile, videoFile);
-
-            // On rename, MediaProvider database entry for pdfFile should be updated with new
-            // videoFile path and mime type should be updated to video/mp4.
-            assertThat(getFileMimeTypeFromDatabase(videoFile)).isEqualTo("video/mp4");
-        } finally {
-            pdfFile.delete();
-            videoFile.delete();
-        }
-    }
-
-    /**
-     * Test that renaming files overwrites files in newPath.
-     */
-    @Test
-    public void testRenameAndReplaceFile() throws Exception {
-        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
-        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
-        final ContentResolver cr = getContentResolver();
-        try {
-            assertThat(videoFile1.createNewFile()).isTrue();
-            assertThat(videoFile2.createNewFile()).isTrue();
-            final Uri uriVideoFile1 = MediaStore.scanFile(cr, videoFile1);
-            final Uri uriVideoFile2 = MediaStore.scanFile(cr, videoFile2);
-
-            // Renaming a file which replaces file in newPath videoFile2 is allowed.
-            assertCanRenameFile(videoFile1, videoFile2);
-
-            // Uri of videoFile2 should be accessible after rename.
-            assertThat(cr.openFileDescriptor(uriVideoFile2, "rw")).isNotNull();
-            // Uri of videoFile1 should not be accessible after rename.
-            assertThrows(FileNotFoundException.class,
-                    () -> { cr.openFileDescriptor(uriVideoFile1, "rw"); });
-        } finally {
-            videoFile1.delete();
-            videoFile2.delete();
-        }
-    }
-
-    /**
-     * Test that app without write permission for file can't update the file.
-     */
-    @Test
-    public void testRenameFileNotOwned() throws Exception {
-        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
-        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
-        try {
-            installApp(TEST_APP_A);
-            assertThat(createFileAs(TEST_APP_A, videoFile1.getAbsolutePath())).isTrue();
-            // App can't rename a file owned by TEST_APP_A.
-            assertCantRenameFile(videoFile1, videoFile2);
-
-            assertThat(videoFile2.createNewFile()).isTrue();
-            // App can't rename a file to videoFile1 which is owned by TEST_APP_A
-            assertCantRenameFile(videoFile2, videoFile1);
-            // TODO(b/146346138): Test that app with right URI permission should be able to rename
-            // the corresponding file
-        } finally {
-            deleteFileAsNoThrow(TEST_APP_A, videoFile1.getAbsolutePath());
-            videoFile2.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that renaming directories is allowed and aligns to default directory restrictions.
-     */
-    @Test
-    public void testRenameDirectory() throws Exception {
-        final File dcimDir = getDcimDir();
-        final File downloadDir = getDownloadDir();
-        final String nonMediaDirectoryName = TEST_DIRECTORY_NAME + "NonMedia";
-        final File nonMediaDirectory = new File(downloadDir, nonMediaDirectoryName);
-        final File pdfFile = new File(nonMediaDirectory, NONMEDIA_FILE_NAME);
-
-        final String mediaDirectoryName = TEST_DIRECTORY_NAME + "Media";
-        final File mediaDirectory1 = new File(dcimDir, mediaDirectoryName);
-        final File videoFile1 = new File(mediaDirectory1, VIDEO_FILE_NAME);
-        final File mediaDirectory2 = new File(downloadDir, mediaDirectoryName);
-        final File videoFile2 = new File(mediaDirectory2, VIDEO_FILE_NAME);
-        final File mediaDirectory3 = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
-        final File videoFile3 = new File(mediaDirectory3, VIDEO_FILE_NAME);
-        final File mediaDirectory4 = new File(mediaDirectory3, mediaDirectoryName);
-
-        try {
-            if (!nonMediaDirectory.exists()) {
-                assertThat(nonMediaDirectory.mkdirs()).isTrue();
-            }
-            assertThat(pdfFile.createNewFile()).isTrue();
-            // Move directory with pdf file to DCIM directory is not allowed.
-            assertThat(nonMediaDirectory.renameTo(new File(dcimDir, nonMediaDirectoryName)))
-                    .isFalse();
-
-            if (!mediaDirectory1.exists()) {
-                assertThat(mediaDirectory1.mkdirs()).isTrue();
-            }
-            assertThat(videoFile1.createNewFile()).isTrue();
-            // Renaming to and from default directories is not allowed.
-            assertThat(mediaDirectory1.renameTo(dcimDir)).isFalse();
-            // Moving top level default directories is not allowed.
-            assertCantRenameDirectory(downloadDir, new File(dcimDir, TEST_DIRECTORY_NAME), null);
-
-            // Moving media directory to Download directory is allowed.
-            assertCanRenameDirectory(mediaDirectory1, mediaDirectory2, new File[] {videoFile1},
-                    new File[] {videoFile2});
-
-            // Moving media directory to Movies directory and renaming directory in new path is
-            // allowed.
-            assertCanRenameDirectory(mediaDirectory2, mediaDirectory3, new File[] {videoFile2},
-                    new File[] {videoFile3});
-
-            // Can't rename a mediaDirectory to non empty non Media directory.
-            assertCantRenameDirectory(mediaDirectory3, nonMediaDirectory, new File[] {videoFile3});
-            // Can't rename a file to a directory.
-            assertCantRenameFile(videoFile3, mediaDirectory3);
-            // Can't rename a directory to file.
-            assertCantRenameDirectory(mediaDirectory3, pdfFile, null);
-            if (!mediaDirectory4.exists()) {
-                assertThat(mediaDirectory4.mkdir()).isTrue();
-            }
-            // Can't rename a directory to subdirectory of itself.
-            assertCantRenameDirectory(mediaDirectory3, mediaDirectory4, new File[] {videoFile3});
-
-        } finally {
-            pdfFile.delete();
-            nonMediaDirectory.delete();
-
-            videoFile1.delete();
-            videoFile2.delete();
-            videoFile3.delete();
-            mediaDirectory1.delete();
-            mediaDirectory2.delete();
-            mediaDirectory3.delete();
-            mediaDirectory4.delete();
-        }
-    }
-
-    /**
-     * Test that renaming directory checks file ownership permissions.
-     */
-    @Test
-    public void testRenameDirectoryNotOwned() throws Exception {
-        final String mediaDirectoryName = TEST_DIRECTORY_NAME + "Media";
-        File mediaDirectory1 = new File(getDcimDir(), mediaDirectoryName);
-        File mediaDirectory2 = new File(getMoviesDir(), mediaDirectoryName);
-        File videoFile = new File(mediaDirectory1, VIDEO_FILE_NAME);
-
-        try {
-            installApp(TEST_APP_A);
-
-            if (!mediaDirectory1.exists()) {
-                assertThat(mediaDirectory1.mkdirs()).isTrue();
-            }
-            assertThat(createFileAs(TEST_APP_A, videoFile.getAbsolutePath())).isTrue();
-            // App doesn't have access to videoFile1, can't rename mediaDirectory1.
-            assertThat(mediaDirectory1.renameTo(mediaDirectory2)).isFalse();
-            assertThat(videoFile.exists()).isTrue();
-            // Test app can delete the file since the file is not moved to new directory.
-            assertThat(deleteFileAs(TEST_APP_A, videoFile.getAbsolutePath())).isTrue();
-        } finally {
-            deleteFileAsNoThrow(TEST_APP_A, videoFile.getAbsolutePath());
-            uninstallAppNoThrow(TEST_APP_A);
-            mediaDirectory1.delete();
-        }
-    }
-
-    /**
-     * Test renaming empty directory is allowed
-     */
-    @Test
-    public void testRenameEmptyDirectory() throws Exception {
-        final String emptyDirectoryName = TEST_DIRECTORY_NAME + "Media";
-        File emptyDirectoryOldPath = new File(getDcimDir(), emptyDirectoryName);
-        File emptyDirectoryNewPath = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
-        try {
-            if (emptyDirectoryOldPath.exists()) {
-                executeShellCommand("rm -r " + emptyDirectoryOldPath.getPath());
-            }
-            assertThat(emptyDirectoryOldPath.mkdirs()).isTrue();
-            assertCanRenameDirectory(emptyDirectoryOldPath, emptyDirectoryNewPath, null, null);
-        } finally {
-            emptyDirectoryOldPath.delete();
-            emptyDirectoryNewPath.delete();
-        }
-    }
-
-    /**
+   /**
      * Test that we don't allow renaming to top level directory
      */
     @Test
@@ -1721,168 +283,6 @@
     }
 
     /**
-     * Tests that an instant app can't access external storage.
-     */
-    @Test
-    @AppModeInstant
-    public void testInstantAppsCantAccessExternalStorage() throws Exception {
-        assumeTrue("This test requires that the test runs as an Instant app",
-                getContext().getPackageManager().isInstantApp());
-        assertThat(getContext().getPackageManager().isInstantApp()).isTrue();
-
-        // Can't read ExternalStorageDir
-        assertThat(getExternalStorageDir().list()).isNull();
-
-        // Can't create a top-level direcotry
-        final File topLevelDir = new File(getExternalStorageDir(), TEST_DIRECTORY_NAME);
-        assertThat(topLevelDir.mkdir()).isFalse();
-
-        // Can't create file under root dir
-        final File newTxtFile = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
-        assertThrows(IOException.class,
-                () -> { newTxtFile.createNewFile(); });
-
-        // Can't create music file under /MUSIC
-        final File newMusicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
-        assertThrows(IOException.class,
-                () -> { newMusicFile.createNewFile(); });
-
-        // getExternalFilesDir() is not null
-        assertThat(getExternalFilesDir()).isNotNull();
-
-        // Can't read/write app specific dir
-        assertThat(getExternalFilesDir().list()).isNull();
-        assertThat(getExternalFilesDir().exists()).isFalse();
-    }
-
-    /**
-     * Test that apps can create and delete hidden file.
-     */
-    @Test
-    public void testCanCreateHiddenFile() throws Exception {
-        final File hiddenImageFile = new File(getDownloadDir(), ".hiddenFile" + IMAGE_FILE_NAME);
-        try {
-            assertThat(hiddenImageFile.createNewFile()).isTrue();
-            // Write to hidden file is allowed.
-            try (final FileOutputStream fos = new FileOutputStream(hiddenImageFile)) {
-                fos.write(BYTES_DATA1);
-            }
-            assertFileContent(hiddenImageFile, BYTES_DATA1);
-
-            assertNotMediaTypeImage(hiddenImageFile);
-
-            assertDirectoryContains(getDownloadDir(), hiddenImageFile);
-            assertThat(getFileRowIdFromDatabase(hiddenImageFile)).isNotEqualTo(-1);
-
-            // We can delete hidden file
-            assertThat(hiddenImageFile.delete()).isTrue();
-            assertThat(hiddenImageFile.exists()).isFalse();
-        } finally {
-            hiddenImageFile.delete();
-        }
-    }
-
-    /**
-     * Test that apps can rename a hidden file.
-     */
-    @Test
-    public void testCanRenameHiddenFile() throws Exception {
-        final String hiddenFileName = ".hidden" + IMAGE_FILE_NAME;
-        final File hiddenImageFile1 = new File(getDcimDir(), hiddenFileName);
-        final File hiddenImageFile2 = new File(getDownloadDir(), hiddenFileName);
-        final File imageFile = new File(getDownloadDir(), IMAGE_FILE_NAME);
-        try {
-            assertThat(hiddenImageFile1.createNewFile()).isTrue();
-            assertCanRenameFile(hiddenImageFile1, hiddenImageFile2);
-            assertNotMediaTypeImage(hiddenImageFile2);
-
-            // We can also rename hidden file to non-hidden
-            assertCanRenameFile(hiddenImageFile2, imageFile);
-            assertIsMediaTypeImage(imageFile);
-
-            // We can rename non-hidden file to hidden
-            assertCanRenameFile(imageFile, hiddenImageFile1);
-            assertNotMediaTypeImage(hiddenImageFile1);
-        } finally {
-            hiddenImageFile1.delete();
-            hiddenImageFile2.delete();
-            imageFile.delete();
-        }
-    }
-
-    /**
-     * Test that files in hidden directory have MEDIA_TYPE=MEDIA_TYPE_NONE
-     */
-    @Test
-    public void testHiddenDirectory() throws Exception {
-        final File hiddenDir = new File(getDownloadDir(), ".hidden" + TEST_DIRECTORY_NAME);
-        final File hiddenImageFile = new File(hiddenDir, IMAGE_FILE_NAME);
-        final File nonHiddenDir = new File(getDownloadDir(), TEST_DIRECTORY_NAME);
-        final File imageFile = new File(nonHiddenDir, IMAGE_FILE_NAME);
-        try {
-            if (!hiddenDir.exists()) {
-                assertThat(hiddenDir.mkdir()).isTrue();
-            }
-            assertThat(hiddenImageFile.createNewFile()).isTrue();
-
-            assertNotMediaTypeImage(hiddenImageFile);
-
-            // Renaming hiddenDir to nonHiddenDir makes the imageFile non-hidden and vice versa
-            assertCanRenameDirectory(
-                    hiddenDir, nonHiddenDir, new File[] {hiddenImageFile}, new File[] {imageFile});
-            assertIsMediaTypeImage(imageFile);
-
-            assertCanRenameDirectory(
-                    nonHiddenDir, hiddenDir, new File[] {imageFile}, new File[] {hiddenImageFile});
-            assertNotMediaTypeImage(hiddenImageFile);
-        } finally {
-            hiddenImageFile.delete();
-            imageFile.delete();
-            hiddenDir.delete();
-            nonHiddenDir.delete();
-        }
-    }
-
-    /**
-     * Test that files in directory with nomedia have MEDIA_TYPE=MEDIA_TYPE_NONE
-     */
-    @Test
-    public void testHiddenDirectory_nomedia() throws Exception {
-        final File directoryNoMedia = new File(getDownloadDir(), "nomedia" + TEST_DIRECTORY_NAME);
-        final File noMediaFile = new File(directoryNoMedia, ".nomedia");
-        final File imageFile = new File(directoryNoMedia, IMAGE_FILE_NAME);
-        final File videoFile = new File(directoryNoMedia, VIDEO_FILE_NAME);
-        try {
-            if (!directoryNoMedia.exists()) {
-                assertThat(directoryNoMedia.mkdir()).isTrue();
-            }
-            assertThat(noMediaFile.createNewFile()).isTrue();
-            assertThat(imageFile.createNewFile()).isTrue();
-
-            assertNotMediaTypeImage(imageFile);
-
-            // Deleting the .nomedia file makes the parent directory non hidden.
-            noMediaFile.delete();
-            MediaStore.scanFile(getContentResolver(), directoryNoMedia);
-            assertIsMediaTypeImage(imageFile);
-
-            // Creating the .nomedia file makes the parent directory hidden again
-            assertThat(noMediaFile.createNewFile()).isTrue();
-            MediaStore.scanFile(getContentResolver(), directoryNoMedia);
-            assertNotMediaTypeImage(imageFile);
-
-            // Renaming the .nomedia file to non hidden file makes the parent directory non hidden.
-            assertCanRenameFile(noMediaFile, videoFile);
-            assertIsMediaTypeImage(imageFile);
-        } finally {
-            noMediaFile.delete();
-            imageFile.delete();
-            videoFile.delete();
-            directoryNoMedia.delete();
-        }
-    }
-
-    /**
      * b/168830497: Test that app can write to file in DCIM/Camera even with .nomedia presence
      */
     @Test
@@ -1924,193 +324,6 @@
         }
     }
 
-    /**
-     * Test that only file manager and app that created the hidden file can list it.
-     */
-    @Test
-    public void testListHiddenFile() throws Exception {
-        final File dcimDir = getDcimDir();
-        final String hiddenImageFileName = ".hidden" + IMAGE_FILE_NAME;
-        final File hiddenImageFile = new File(dcimDir, hiddenImageFileName);
-        try {
-            assertThat(hiddenImageFile.createNewFile()).isTrue();
-            assertNotMediaTypeImage(hiddenImageFile);
-
-            assertDirectoryContains(dcimDir, hiddenImageFile);
-
-            installApp(TEST_APP_A, true);
-            // TestApp with read permissions can't see the hidden image file created by other app
-            assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
-                    .doesNotContain(hiddenImageFileName);
-
-            final int testAppUid =
-                    getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
-            // FileManager can see the hidden image file created by other app
-            try {
-                allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
-                assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
-                        .contains(hiddenImageFileName);
-            } finally {
-                denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
-            }
-
-            // Gallery can not see the hidden image file created by other app
-            try {
-                allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
-                assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
-                        .doesNotContain(hiddenImageFileName);
-            } finally {
-                denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
-            }
-        } finally {
-            hiddenImageFile.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    @Test
-    public void testOpenPendingAndTrashed() throws Exception {
-        final File pendingImageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
-        final File trashedVideoFile = new File(getPicturesDir(), VIDEO_FILE_NAME);
-        final File pendingPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
-        final File trashedPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        Uri pendingImgaeFileUri = null;
-        Uri trashedVideoFileUri = null;
-        Uri pendingPdfFileUri = null;
-        Uri trashedPdfFileUri = null;
-        try {
-            installAppWithStoragePermissions(TEST_APP_A);
-
-            pendingImgaeFileUri = createPendingFile(pendingImageFile);
-            assertOpenPendingOrTrashed(pendingImgaeFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
-
-            pendingPdfFileUri = createPendingFile(pendingPdfFile);
-            assertOpenPendingOrTrashed(pendingPdfFileUri, TEST_APP_A,
-                    /*isImageOrVideo*/ false);
-
-            trashedVideoFileUri = createTrashedFile(trashedVideoFile);
-            assertOpenPendingOrTrashed(trashedVideoFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
-
-            trashedPdfFileUri = createTrashedFile(trashedPdfFile);
-            assertOpenPendingOrTrashed(trashedPdfFileUri, TEST_APP_A,
-                    /*isImageOrVideo*/ false);
-
-        } finally {
-            deleteFiles(pendingImageFile, pendingImageFile, trashedVideoFile,
-                    trashedPdfFile);
-            deleteWithMediaProviderNoThrow(pendingImgaeFileUri, trashedVideoFileUri,
-                    pendingPdfFileUri, trashedPdfFileUri);
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    @Test
-    public void testListPendingAndTrashed() throws Exception {
-        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
-        final File pdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        Uri imageFileUri = null;
-        Uri pdfFileUri = null;
-        try {
-            installAppWithStoragePermissions(TEST_APP_A);
-
-            imageFileUri = createPendingFile(imageFile);
-            // Check that only owner package, file manager and system gallery can list pending image
-            // file.
-            assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
-                    /*isImageOrVideo*/ true);
-
-            trashFile(imageFileUri);
-            // Check that only owner package, file manager and system gallery can list trashed image
-            // file.
-            assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
-                    /*isImageOrVideo*/ true);
-
-            pdfFileUri = createPendingFile(pdfFile);
-            // Check that only owner package, file manager can list pending non media file.
-            assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
-                    /*isImageOrVideo*/ false);
-
-            trashFile(pdfFileUri);
-            // Check that only owner package, file manager can list trashed non media file.
-            assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
-                    /*isImageOrVideo*/ false);
-        } finally {
-            deleteWithMediaProviderNoThrow(imageFileUri, pdfFileUri);
-            deleteFiles(imageFile, pdfFile);
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    @Test
-    public void testDeletePendingAndTrashed() throws Exception {
-        final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
-        final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
-        final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        final File trashedPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
-        // Actual path of the file gets rewritten for pending and trashed files.
-        String pendingVideoFilePath = null;
-        String trashedImageFilePath = null;
-        String pendingPdfFilePath = null;
-        String trashedPdfFilePath = null;
-        try {
-            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
-            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
-            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
-            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
-
-            // App can delete its own pending and trashed file.
-            assertCanDeletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
-                    trashedPdfFilePath);
-
-            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
-            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
-            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
-            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
-
-            installAppWithStoragePermissions(TEST_APP_A);
-
-            // App can't delete other app's pending and trashed file.
-            assertCantDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
-                    pendingPdfFilePath, trashedPdfFilePath);
-
-            final int testAppUid =
-                    getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
-            try {
-                allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
-                // File Manager can delete any pending and trashed file
-                assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
-                        pendingPdfFilePath, trashedPdfFilePath);
-            } finally {
-                denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
-            }
-
-            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
-            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
-            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
-            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
-
-            try {
-                allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
-                // System Gallery can delete any pending and trashed image or video file.
-                assertTrue(isMediaTypeImageOrVideo(new File(pendingVideoFilePath)));
-                assertTrue(isMediaTypeImageOrVideo(new File(trashedImageFilePath)));
-                assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath);
-
-                // System Gallery can't delete other app's pending and trashed pdf file.
-                assertFalse(isMediaTypeImageOrVideo(new File(pendingPdfFilePath)));
-                assertFalse(isMediaTypeImageOrVideo(new File(trashedPdfFilePath)));
-                assertCantDeletePathsAs(TEST_APP_A, pendingPdfFilePath, trashedPdfFilePath);
-            } finally {
-                denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
-            }
-        } finally {
-            deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
-                    trashedPdfFilePath);
-            deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
     @Test
     public void testManageExternalStorageCanDeleteOtherAppsContents() throws Exception {
         pollForManageExternalStorageAllowed();
@@ -2354,235 +567,6 @@
     }
 
     @Test
-    public void testQueryOtherAppsFiles() throws Exception {
-        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
-        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
-        try {
-            installApp(TEST_APP_A);
-            // Apps can't query other app's pending file, hence create file and publish it.
-            assertCreatePublishedFilesAs(
-                    TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
-
-            // Since the test doesn't have READ_EXTERNAL_STORAGE nor any other special permissions,
-            // it can't query for another app's contents.
-            assertCantQueryFile(otherAppImg);
-            assertCantQueryFile(otherAppMusic);
-            assertCantQueryFile(otherAppPdf);
-            assertCantQueryFile(otherHiddenFile);
-        } finally {
-            deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
-            uninstallApp(TEST_APP_A);
-        }
-    }
-
-    @Test
-    public void testSystemGalleryQueryOtherAppsFiles() throws Exception {
-        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
-        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
-        try {
-            installApp(TEST_APP_A);
-            // Apps can't query other app's pending file, hence create file and publish it.
-            assertCreatePublishedFilesAs(
-                    TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
-
-            // System gallery apps have access to video and image files
-            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-
-            assertCanQueryAndOpenFile(otherAppImg, "rw");
-            // System gallery doesn't have access to hidden image files of other app
-            assertCantQueryFile(otherHiddenFile);
-            // But no access to PDFs or music files
-            assertCantQueryFile(otherAppMusic);
-            assertCantQueryFile(otherAppPdf);
-        } finally {
-            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-            deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
-            uninstallApp(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that System Gallery app can rename any directory under the default directories
-     * designated for images and videos, even if they contain other apps' contents that
-     * System Gallery doesn't have read access to.
-     */
-    @Test
-    public void testSystemGalleryCanRenameImageAndVideoDirs() throws Exception {
-        final File dirInDcim = new File(getDcimDir(), TEST_DIRECTORY_NAME);
-        final File dirInPictures = new File(getPicturesDir(), TEST_DIRECTORY_NAME);
-        final File dirInPodcasts = new File(getPodcastsDir(), TEST_DIRECTORY_NAME);
-        final File otherAppImageFile1 = new File(dirInDcim, "other_" + IMAGE_FILE_NAME);
-        final File otherAppVideoFile1 = new File(dirInDcim, "other_" + VIDEO_FILE_NAME);
-        final File otherAppPdfFile1 = new File(dirInDcim, "other_" + NONMEDIA_FILE_NAME);
-        final File otherAppImageFile2 = new File(dirInPictures, "other_" + IMAGE_FILE_NAME);
-        final File otherAppVideoFile2 = new File(dirInPictures, "other_" + VIDEO_FILE_NAME);
-        final File otherAppPdfFile2 = new File(dirInPictures, "other_" + NONMEDIA_FILE_NAME);
-        try {
-            assertThat(dirInDcim.exists() || dirInDcim.mkdir()).isTrue();
-
-            executeShellCommand("touch " + otherAppPdfFile1);
-            MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
-
-            installAppWithStoragePermissions(TEST_APP_A);
-            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-
-            assertCreateFilesAs(TEST_APP_A, otherAppImageFile1, otherAppVideoFile1);
-
-            // System gallery privileges don't go beyond DCIM, Movies and Pictures boundaries.
-            assertCantRenameDirectory(dirInDcim, dirInPodcasts, /*oldFilesList*/ null);
-
-            // Rename should succeed, but System Gallery still can't access that PDF file!
-            assertCanRenameDirectory(dirInDcim, dirInPictures,
-                    new File[] {otherAppImageFile1, otherAppVideoFile1},
-                    new File[] {otherAppImageFile2, otherAppVideoFile2});
-            assertThat(getFileRowIdFromDatabase(otherAppPdfFile1)).isEqualTo(-1);
-            assertThat(getFileRowIdFromDatabase(otherAppPdfFile2)).isEqualTo(-1);
-        } finally {
-            executeShellCommand("rm " + otherAppPdfFile1);
-            executeShellCommand("rm " + otherAppPdfFile2);
-            MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
-            MediaStore.scanFile(getContentResolver(), otherAppPdfFile2);
-            otherAppImageFile1.delete();
-            otherAppImageFile2.delete();
-            otherAppVideoFile1.delete();
-            otherAppVideoFile2.delete();
-            otherAppPdfFile1.delete();
-            otherAppPdfFile2.delete();
-            dirInDcim.delete();
-            dirInPictures.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-            denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
-        }
-    }
-
-    /**
-     * Test that row ID corresponding to deleted path is restored on subsequent create.
-     */
-    @Test
-    public void testCreateCanRestoreDeletedRowId() throws Exception {
-        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
-        final ContentResolver cr = getContentResolver();
-
-        try {
-            assertThat(imageFile.createNewFile()).isTrue();
-            final long oldRowId = getFileRowIdFromDatabase(imageFile);
-            assertThat(oldRowId).isNotEqualTo(-1);
-            final Uri uriOfOldFile = MediaStore.scanFile(cr, imageFile);
-            assertThat(uriOfOldFile).isNotNull();
-
-            assertThat(imageFile.delete()).isTrue();
-            // We should restore old row Id corresponding to deleted imageFile.
-            assertThat(imageFile.createNewFile()).isTrue();
-            assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(oldRowId);
-            assertThat(cr.openFileDescriptor(uriOfOldFile, "rw")).isNotNull();
-
-            assertThat(imageFile.delete()).isTrue();
-            installApp(TEST_APP_A);
-            assertThat(createFileAs(TEST_APP_A, imageFile.getAbsolutePath())).isTrue();
-
-            final Uri uriOfNewFile = MediaStore.scanFile(getContentResolver(), imageFile);
-            assertThat(uriOfNewFile).isNotNull();
-            // We shouldn't restore deleted row Id if delete & create are called from different apps
-            assertThat(Integer.getInteger(uriOfNewFile.getLastPathSegment())).isNotEqualTo(oldRowId);
-        } finally {
-            imageFile.delete();
-            deleteFileAsNoThrow(TEST_APP_A, imageFile.getAbsolutePath());
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
-    /**
-     * Test that row ID corresponding to deleted path is restored on subsequent rename.
-     */
-    @Test
-    public void testRenameCanRestoreDeletedRowId() throws Exception {
-        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
-        final File temporaryFile = new File(getDownloadDir(), IMAGE_FILE_NAME + "_.tmp");
-        final ContentResolver cr = getContentResolver();
-
-        try {
-            assertThat(imageFile.createNewFile()).isTrue();
-            final Uri oldUri = MediaStore.scanFile(cr, imageFile);
-            assertThat(oldUri).isNotNull();
-
-            Files.copy(imageFile, temporaryFile);
-            assertThat(imageFile.delete()).isTrue();
-            assertCanRenameFile(temporaryFile, imageFile);
-
-            final Uri newUri = MediaStore.scanFile(cr, imageFile);
-            assertThat(newUri).isNotNull();
-            assertThat(newUri.getLastPathSegment()).isEqualTo(oldUri.getLastPathSegment());
-            // oldUri of imageFile is still accessible after delete and rename.
-            assertThat(cr.openFileDescriptor(oldUri, "rw")).isNotNull();
-        } finally {
-            imageFile.delete();
-            temporaryFile.delete();
-        }
-    }
-
-    @Test
-    public void testCantCreateOrRenameFileWithInvalidName() throws Exception {
-        File invalidFile = new File(getDownloadDir(), "<>");
-        File validFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
-        try {
-            assertThrows(IOException.class, "Operation not permitted",
-                    () -> { invalidFile.createNewFile(); });
-
-            assertThat(validFile.createNewFile()).isTrue();
-            // We can't rename a file to a file name with invalid FAT characters.
-            assertCantRenameFile(validFile, invalidFile);
-        } finally {
-            invalidFile.delete();
-            validFile.delete();
-        }
-    }
-
-    @Test
-    public void testRenameWithSpecialChars() throws Exception {
-        final String specialCharsSuffix = "'`~!@#$%^& ()_+-={}[];'.)";
-
-        final File fileSpecialChars =
-                new File(getDownloadDir(), NONMEDIA_FILE_NAME + specialCharsSuffix);
-
-        final File dirSpecialChars =
-                new File(getDownloadDir(), TEST_DIRECTORY_NAME + specialCharsSuffix);
-        final File file1 = new File(dirSpecialChars, NONMEDIA_FILE_NAME);
-        final File fileSpecialChars1 =
-                new File(dirSpecialChars, NONMEDIA_FILE_NAME + specialCharsSuffix);
-
-        final File renamedDir = new File(getDocumentsDir(), TEST_DIRECTORY_NAME);
-        final File file2 = new File(renamedDir, NONMEDIA_FILE_NAME);
-        final File fileSpecialChars2 =
-                new File(renamedDir, NONMEDIA_FILE_NAME + specialCharsSuffix);
-        try {
-            assertTrue(fileSpecialChars.createNewFile());
-            if (!dirSpecialChars.exists()) {
-                assertTrue(dirSpecialChars.mkdir());
-            }
-            assertTrue(file1.createNewFile());
-
-            // We can rename file name with special characters
-            assertCanRenameFile(fileSpecialChars, fileSpecialChars1);
-
-            // We can rename directory name with special characters
-            assertCanRenameDirectory(dirSpecialChars, renamedDir,
-                    new File[] {file1, fileSpecialChars1}, new File[] {file2, fileSpecialChars2});
-        } finally {
-            file1.delete();
-            file2.delete();
-            fileSpecialChars.delete();
-            fileSpecialChars1.delete();
-            fileSpecialChars2.delete();
-            dirSpecialChars.delete();
-            renamedDir.delete();
-        }
-    }
-
-    @Test
     public void testAndroidMedia() throws Exception {
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
 
@@ -2672,51 +656,10 @@
         }
     }
 
-    /**
-     * Test that IS_PENDING is set for files created via filepath
-     */
-    @Test
-    public void testPendingFromFuse() throws Exception {
-        final File pendingFile = new File(getDcimDir(), IMAGE_FILE_NAME);
-        final File otherPendingFile = new File(getDcimDir(), VIDEO_FILE_NAME);
-        try {
-            assertTrue(pendingFile.createNewFile());
-            // Newly created file should have IS_PENDING set
-            try (Cursor c = queryFile(pendingFile, MediaStore.MediaColumns.IS_PENDING)) {
-                assertTrue(c.moveToFirst());
-                assertThat(c.getInt(0)).isEqualTo(1);
-            }
-
-            // If we query with MATCH_EXCLUDE, we should still see this pendingFile
-            try (Cursor c = queryFileExcludingPending(pendingFile, MediaColumns.IS_PENDING)) {
-                assertThat(c.getCount()).isEqualTo(1);
-                assertTrue(c.moveToFirst());
-                assertThat(c.getInt(0)).isEqualTo(1);
-            }
-
-            assertNotNull(MediaStore.scanFile(getContentResolver(), pendingFile));
-
-            // IS_PENDING should be unset after the scan
-            try (Cursor c = queryFile(pendingFile, MediaStore.MediaColumns.IS_PENDING)) {
-                assertTrue(c.moveToFirst());
-                assertThat(c.getInt(0)).isEqualTo(0);
-            }
-
-            installAppWithStoragePermissions(TEST_APP_A);
-            assertCreateFilesAs(TEST_APP_A, otherPendingFile);
-            // We can't query other apps pending file from FUSE with MATCH_EXCLUDE
-            try (Cursor c = queryFileExcludingPending(otherPendingFile, MediaColumns.IS_PENDING)) {
-                assertThat(c.getCount()).isEqualTo(0);
-            }
-        } finally {
-            pendingFile.delete();
-            deleteFileAsNoThrow(TEST_APP_A, otherPendingFile.getAbsolutePath());
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
 
     @Test
     public void testOpenOtherPendingFilesFromFuse() throws Exception {
+        pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         final File otherPendingFile = new File(getDcimDir(), IMAGE_FILE_NAME);
         try {
             installApp(TEST_APP_A);
@@ -2733,34 +676,6 @@
         }
     }
 
-    /**
-     * Test that apps can't set attributes on another app's files.
-     */
-    @Test
-    public void testCantSetAttrOtherAppsFile() throws Exception {
-        // This path's permission is checked in MediaProvider (directory/external media dir)
-        final File externalMediaPath = new File(getExternalMediaDir(), VIDEO_FILE_NAME);
-
-        try {
-            // Create the files
-            if (!externalMediaPath.exists()) {
-                assertThat(externalMediaPath.createNewFile()).isTrue();
-            }
-
-            // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
-            installAppWithStoragePermissions(TEST_APP_A);
-
-            // TEST_APP_A should not be able to setattr to other app's files.
-            assertWithMessage(
-                "setattr on directory/external media path [%s]", externalMediaPath.getPath())
-                .that(setAttrAs(TEST_APP_A, externalMediaPath.getPath()))
-                .isFalse();
-        } finally {
-            externalMediaPath.delete();
-            uninstallAppNoThrow(TEST_APP_A);
-        }
-    }
-
     @Test
     public void testNoIsolatedStorageCanCreateFilesAnywhere() throws Exception {
         final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
@@ -2952,48 +867,6 @@
     }
 
     /**
-     * b/171768780: Test that scan doesn't skip scanning renamed hidden file.
-     */
-    @Test
-    public void testScanUpdatesMetadataForRenamedHiddenFile() throws Exception {
-        final File hiddenFile = new File(getPicturesDir(), ".hidden_" + IMAGE_FILE_NAME);
-        final File jpgFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
-        try {
-            // Copy the image content to hidden file
-            try (InputStream in =
-                         getContext().getResources().openRawResource(R.raw.img_with_metadata);
-                 FileOutputStream out = new FileOutputStream(hiddenFile)) {
-                FileUtils.copy(in, out);
-                out.getFD().sync();
-            }
-            Uri scanUri = MediaStore.scanFile(getContentResolver(), hiddenFile);
-            assertNotNull(scanUri);
-
-            // Rename hidden file to non-hidden
-            assertCanRenameFile(hiddenFile, jpgFile);
-
-            try (Cursor c = queryFile(jpgFile, MediaStore.MediaColumns.DATE_TAKEN)) {
-                assertTrue(c.moveToFirst());
-                // The file is not scanned yet, hence the metadata is not updated yet.
-                assertThat(c.getString(0)).isNull();
-            }
-
-            // Scan the file to update the metadata for renamed hidden file.
-            scanUri = MediaStore.scanFile(getContentResolver(), jpgFile);
-            assertNotNull(scanUri);
-
-            // Scan should be able to update metadata even if File.lastModifiedTime hasn't changed.
-            try (Cursor c = queryFile(jpgFile, MediaStore.MediaColumns.DATE_TAKEN)) {
-                assertTrue(c.moveToFirst());
-                assertThat(c.getString(0)).isNotNull();
-            }
-        } finally {
-            hiddenFile.delete();
-            jpgFile.delete();
-        }
-    }
-
-    /**
      * Checks restrictions for opening pending and trashed files by different apps. Assumes that
      * given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
      * method doesn't uninstall given {@code testApp} at the end.