Test Scoped Storage on public volumes

Add a new CTS test: CtsScopedStoragePublicVolumeHostTest
The new test inherits from the existing ScopedStorageTest and runs all
of the test cases there but on a public volume.

In addition, refactor some of the utilities to make that possible.

Test: atest CtsScopedStoragePublicVolumeHostTest --rebuild-module-info
Bug: 148841336

Change-Id: I933a4e32cf09579c8906cf1f25e41da773b62a4c
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index 1d4b77a..3e5096b 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -79,3 +79,12 @@
     test_suites: ["general-tests", "mts"],
     test_config: "AndroidTest.xml",
 }
+
+java_test_host {
+    name: "CtsScopedStoragePublicVolumeHostTest",
+    srcs: ["host/src/**/*.java"],
+    libs: ["tradefed"],
+    static_libs: ["testng"],
+    test_suites: ["general-tests", "mts"],
+    test_config: "PublicVolumeTest.xml",
+}
diff --git a/hostsidetests/scopedstorage/PublicVolumeTest.xml b/hostsidetests/scopedstorage/PublicVolumeTest.xml
new file mode 100644
index 0000000..1761695
--- /dev/null
+++ b/hostsidetests/scopedstorage/PublicVolumeTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="External storage host test that runs on a public volume">
+    <option name="test-suite-tag" value="cts" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="ScopedStorageTest.apk" />
+        <option name="test-file-name" value="LegacyStorageTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="class" value="android.scopedstorage.cts.host.PublicVolumeHostTest" />
+    </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.mediaprovider" />
+    </object>
+</configuration>
diff --git a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
index 86a7096..2054907 100644
--- a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
+++ b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
@@ -31,12 +31,15 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Environment;
+
+import androidx.annotation.Nullable;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Helper app for ScopedStorageTest.
@@ -46,8 +49,13 @@
  */
 public class ScopedStorageTestHelper extends Activity {
     private static final String TAG = "ScopedStorageTestHelper";
-    private static final File ANDROID_DIR =
-            new File(Environment.getExternalStorageDirectory(), "Android");
+    /**
+     * Regex that matches paths in all well-known package-specific directories,
+     * and which captures the directory type as the first group (data|media|obb) and the
+     * package name as the 2nd group.
+     */
+    private static final Pattern PATTERN_OWNED_PATH = Pattern.compile(
+            "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(data|media|obb)/([^/]+)(/?.*)?");
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -143,17 +151,14 @@
     }
 
     private void maybeCreateParentDirInAndroid(File file) {
-        if (!file.getAbsolutePath().startsWith(ANDROID_DIR.getAbsolutePath())) {
+        final String ownedPathType = getOwnedDirectoryType(file);
+        if (ownedPathType == null) {
             return;
         }
-        String[] segments = file.getAbsolutePath().split("/");
-        int index = ANDROID_DIR.getAbsolutePath().split("/").length;
-        if (index < segments.length) {
-            // Create the external app dir first.
-            if (createExternalAppDir(segments[index])) {
-                // Then create everything along the path.
-                file.getParentFile().mkdirs();
-            }
+        // Create the external app dir first.
+        if (createExternalAppDir(ownedPathType)) {
+            // Then create everything along the path.
+            file.getParentFile().mkdirs();
         }
     }
 
@@ -162,13 +167,11 @@
         // expected to call one of the following methods.
         switch (name) {
             case "data":
-                getApplicationContext().getExternalFilesDir(null);
-                return true;
-            case "cache":
-                getApplicationContext().getExternalCacheDir();
+                getApplicationContext().getExternalFilesDirs(null);
+                getApplicationContext().getExternalCacheDirs();
                 return true;
             case "obb":
-                getApplicationContext().getObbDir();
+                getApplicationContext().getObbDirs();
                 return true;
             case "media":
                 getApplicationContext().getExternalMediaDirs();
@@ -177,4 +180,16 @@
                 return false;
         }
     }
+
+    /**
+     * Returns null if given path is not an owned path.
+     */
+    @Nullable
+    private static String getOwnedDirectoryType(File path) {
+        final Matcher m = PATTERN_OWNED_PATH.matcher(path.getAbsolutePath());
+        if (m.matches()) {
+            return m.group(1);
+        }
+        return null;
+    }
 }
diff --git a/hostsidetests/scopedstorage/TEST_MAPPING b/hostsidetests/scopedstorage/TEST_MAPPING
index 8f4fbd1..3f87702 100644
--- a/hostsidetests/scopedstorage/TEST_MAPPING
+++ b/hostsidetests/scopedstorage/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "CtsScopedStorageHostTest"
+    },
+    {
+      "name": "CtsScopedStoragePublicVolumeHostTest"
     }
   ]
 }
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java
new file mode 100644
index 0000000..33e3e94
--- /dev/null
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.scopedstorage.cts.host;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.device.ITestDevice;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+
+public class PublicVolumeHostTest extends ScopedStorageHostTest {
+    /** Used to clean up the virtual volume after the test */
+    private static ITestDevice sDevice = null;
+    private boolean mIsPublicVolumeSetup = false;
+    String executeShellCommand(String cmd) throws Exception {
+        return getDevice().executeShellCommand(cmd);
+    }
+
+    private void setupNewPublicVolume() throws Exception {
+        if (!mIsPublicVolumeSetup) {
+            runDeviceTest("setupNewPublicVolume");
+            mIsPublicVolumeSetup = true;
+        }
+    }
+
+    private void setupDevice() {
+        if (sDevice == null) {
+            sDevice = getDevice();
+        }
+    }
+
+    /**
+     * Runs the given phase of PublicVolumeTest by calling into the device.
+     * Throws an exception if the test phase fails.
+     */
+    @Override
+    void runDeviceTest(String phase) throws Exception {
+        assertTrue(runDeviceTests("android.scopedstorage.cts",
+                "android.scopedstorage.cts.PublicVolumeTest", phase));
+    }
+
+    @Before
+    public void setup() throws Exception {
+        setupDevice();
+        setupNewPublicVolume();
+        super.setup();
+    }
+
+    @AfterClass
+    public static void deletePublicVolumes() throws Exception {
+        if (sDevice != null) {
+            sDevice.executeShellCommand("sm set-virtual-disk false");
+        }
+    }
+}
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 1e7a4fe..5d330c3 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -31,25 +31,26 @@
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class ScopedStorageHostTest extends BaseHostJUnit4Test {
-    private boolean isExternalStorageSetup = false;
+    private boolean mIsExternalStorageSetup = false;
 
     /**
-     * Runs the given phase of FilePathAccessTest by calling into the device.
+     * Runs the given phase of ScopedStorageTest by calling into the device.
      * Throws an exception if the test phase fails.
      */
-    private void runDeviceTest(String phase) throws Exception {
+    void runDeviceTest(String phase) throws Exception {
         assertTrue(runDeviceTests("android.scopedstorage.cts",
                 "android.scopedstorage.cts.ScopedStorageTest", phase));
+
     }
 
-    private String executeShellCommand(String cmd) throws Exception {
+    String executeShellCommand(String cmd) throws Exception {
         return getDevice().executeShellCommand(cmd);
     }
 
     private void setupExternalStorage() throws Exception {
-        if (!isExternalStorageSetup) {
+        if (!mIsExternalStorageSetup) {
             runDeviceTest("setupExternalStorage");
-            isExternalStorageSetup = true;
+            mIsExternalStorageSetup = true;
         }
     }
 
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 b23d52f..0650cd4 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
@@ -68,6 +68,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -99,6 +100,7 @@
 
     // Root of external storage
     private static File sExternalStorageDirectory = Environment.getExternalStorageDirectory();
+    private static String sStorageVolumeName = MediaStore.VOLUME_EXTERNAL;
 
     private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
     private static final long POLLING_SLEEP_MILLIS = 100;
@@ -325,14 +327,14 @@
      */
     @Nullable
     public static Uri getFileUri(@NonNull File file) {
-        final Uri contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
+        final Uri contentUri = MediaStore.Files.getContentUri(sStorageVolumeName);
         final int id = getFileRowIdFromDatabase(file);
         return id == -1 ? null : ContentUris.withAppendedId(contentUri, id);
     }
 
     /**
      * Queries {@link ContentResolver} for a file and returns the corresponding row ID for its
-     * entry in the database.
+     * entry in the database. Returns {@code -1} if file is not found.
      */
     public static int getFileRowIdFromDatabase(@NonNull File file) {
         int id = -1;
@@ -360,12 +362,28 @@
     }
 
     /**
+     * Queries {@link ContentResolver} for a file and returns the corresponding file size for its
+     * entry in the database. Returns {@code -1} if file is not found.
+     */
+    @Nullable
+    public static int getFileSizeFromDatabase(@NonNull File file) {
+        int size = -1;
+        try (Cursor c = queryFile(file, MediaStore.MediaColumns.SIZE)) {
+            if (c.moveToFirst()) {
+                size = c.getInt(0);
+            }
+        }
+        return size;
+    }
+
+    /**
      * Queries {@link ContentResolver} for a video file and returns a {@link Cursor} with the given
      * columns.
      */
     @NonNull
     public static Cursor queryVideoFile(File file, String... projection) {
-        return queryFile(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, file, projection);
+        return queryFile(MediaStore.Video.Media.getContentUri(sStorageVolumeName), file,
+                projection);
     }
 
     /**
@@ -374,7 +392,8 @@
      */
     @NonNull
     public static Cursor queryImageFile(File file, String... projection) {
-        return queryFile(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, file, projection);
+        return queryFile(MediaStore.Images.Media.getContentUri(sStorageVolumeName), file,
+                projection);
     }
 
     /**
@@ -416,7 +435,7 @@
      */
     public static void deleteWithMediaProvider(@NonNull File file) {
         assertThat(getContentResolver().delete(
-                           MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
+                           MediaStore.Files.getContentUri(sStorageVolumeName),
                            /*where*/ MediaStore.MediaColumns.DATA + " = ?",
                            /*selectionArgs*/ new String[] {file.getPath()}))
                 .isEqualTo(1);
@@ -448,17 +467,17 @@
         String[] selectionArgs = {relativePath + '/', oldDisplayName};
         String[] projection = {MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA};
 
+        final Uri contentUri = MediaStore.Files.getContentUri(sStorageVolumeName);
         ContentValues values = new ContentValues();
         values.put(MediaStore.MediaColumns.DISPLAY_NAME, newDisplayName);
 
-        try (Cursor cursor =
-                        getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                                projection, selection, selectionArgs, null)) {
+        try (Cursor cursor = getContentResolver().query(contentUri, projection, selection,
+                selectionArgs, null)) {
             assertThat(cursor.getCount()).isEqualTo(1);
             cursor.moveToFirst();
             int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
             String data = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
-            Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
+            Uri uri = ContentUris.withAppendedId(contentUri, id);
             Log.i(TAG, "Uri: " + uri + ". Data: " + data);
             assertThat(getContentResolver().update(uri, values, selection, selectionArgs))
                     .isEqualTo(1);
@@ -665,6 +684,7 @@
     }
 
     public static void setExternalStorageVolume(@NonNull String volName) {
+        sStorageVolumeName = volName.toLowerCase(Locale.ROOT);
         sExternalStorageDirectory = new File("/storage/" + volName);
     }
 
@@ -674,6 +694,7 @@
      * @see Environment#getExternalStorageDirectory()
      */
     public static void resetDefaultExternalStorageVolume() {
+        sStorageVolumeName = MediaStore.VOLUME_EXTERNAL;
         sExternalStorageDirectory = Environment.getExternalStorageDirectory();
     }
 
@@ -953,7 +974,7 @@
     @NonNull
     private static Cursor queryFile(@NonNull File file, String... projection) {
         return queryFile(
-                MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), file, projection);
+                MediaStore.Files.getContentUri(sStorageVolumeName), file, projection);
     }
 
     @NonNull
@@ -971,6 +992,24 @@
         return c;
     }
 
+    /**
+     * Creates a new virtual public volume and returns the volume's name.
+     */
+    public static String createNewPublicVolume() throws Exception {
+        executeShellCommand("sm set-force-adoptable on");
+        executeShellCommand("sm set-virtual-disk true");
+        Thread.sleep(2000);
+        pollForCondition(TestUtils::partitionDisk, "Timed out while waiting for disk partitioning");
+
+        final String[] res = new String[1];
+        pollForCondition(() -> {
+            res[0] = getPublicVolumeName();
+            return res[0] != null;
+        }, "Timed out while waiting for public volume to be created");
+
+        return res[0];
+    }
+
     private static boolean partitionDisk() {
         try {
             final String listDisks = executeShellCommand("sm list-disks").trim();
@@ -981,6 +1020,27 @@
         }
     }
 
+    /**
+     * Gets the name of the public volume.
+     */
+    public static String getPublicVolumeName() {
+        final String[] allVolumeDetails;
+        try {
+            allVolumeDetails = executeShellCommand("sm list-volumes")
+                    .trim().split("\n");
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to execute shell command", e);
+            return null;
+        }
+        for (String volDetails : allVolumeDetails) {
+            if (volDetails.startsWith("public")) {
+                final String[] publicVolumeDetails = volDetails.trim().split(" ");
+                return publicVolumeDetails[publicVolumeDetails.length - 1];
+            }
+        }
+        return null;
+    }
+
     private static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
             throws Exception {
         for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java
new file mode 100644
index 0000000..b0bf57e
--- /dev/null
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.scopedstorage.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.scopedstorage.cts.lib.TestUtils;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs all of the tests from {@link ScopedStorageTest} on a public volume.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PublicVolumeTest extends ScopedStorageTest {
+    @Override
+    @Before
+    public void setup() throws Exception {
+        final String volumeName = TestUtils.getPublicVolumeName();
+        assertThat(volumeName).isNotNull();
+        TestUtils.setExternalStorageVolume(volumeName);
+        super.setup();
+    }
+
+    /**
+     * This is not an actual test, but rather just a one time setup method that creates the new
+     * public volume on which the test would run.
+     */
+    @Test
+    public void setupNewPublicVolume() throws Exception {
+        TestUtils.createNewPublicVolume();
+    }
+}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 606e15a..d65941b 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -58,7 +58,9 @@
 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;
@@ -143,6 +145,11 @@
 import java.util.HashMap;
 import java.util.List;
 
+/**
+ * Runs the scoped storage tests on primary external storage.
+ *
+ * <p>These tests are also run on a public volume by {@link PublicVolumeTest}.
+ */
 @RunWith(AndroidJUnit4.class)
 public class ScopedStorageTest {
     static final String TAG = "ScopedStorageTest";
@@ -317,23 +324,11 @@
     public void testContributeMediaFile() throws Exception {
         final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
 
-        ContentResolver cr = getContentResolver();
-        final String selection =
-                MediaColumns.RELATIVE_PATH + " = ? AND " + MediaColumns.DISPLAY_NAME + " = ?";
-        final String[] selectionArgs = {Environment.DIRECTORY_DCIM + '/', IMAGE_FILE_NAME};
-
         try {
             assertThat(imageFile.createNewFile()).isTrue();
 
             // Ensure that the file was successfully added to the MediaProvider database
-            try (final Cursor c = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                         /* projection */ new String[] {MediaColumns.OWNER_PACKAGE_NAME},
-                         selection, selectionArgs, null)) {
-                assertThat(c.getCount()).isEqualTo(1);
-                c.moveToFirst();
-                assertThat(c.getString(c.getColumnIndex(MediaColumns.OWNER_PACKAGE_NAME)))
-                        .isEqualTo(THIS_PACKAGE_NAME);
-            }
+            assertThat(getFileOwnerPackageFromDatabase(imageFile)).isEqualTo(THIS_PACKAGE_NAME);
 
             // Try to write random data to the file
             try (final FileOutputStream fos = new FileOutputStream(imageFile)) {
@@ -349,23 +344,13 @@
             assertThat(MediaStore.scanFile(getContentResolver(), imageFile)).isNotNull();
 
             // Ensure that the scan was completed and the file's size was updated.
-            try (final Cursor c = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                         /* projection */ new String[] {MediaColumns.SIZE}, selection,
-                         selectionArgs, null)) {
-                assertThat(c.getCount()).isEqualTo(1);
-                c.moveToFirst();
-                assertThat(c.getInt(c.getColumnIndex(MediaColumns.SIZE)))
-                        .isEqualTo(BYTES_DATA1.length + BYTES_DATA2.length);
-            }
+            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.
-        try (final Cursor c = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                     /* projection */ new String[] {MediaColumns.OWNER_PACKAGE_NAME}, selection,
-                     selectionArgs, null)) {
-            assertThat(c.getCount()).isEqualTo(0);
-        }
+        assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(-1);
     }
 
     @Test
@@ -637,26 +622,29 @@
     @Test
     public void testListFilesFromExternalFilesDirectory() throws Exception {
         final String packageName = THIS_PACKAGE_NAME;
-        final File videoFile = new File(getExternalFilesDir(), NONMEDIA_FILE_NAME);
+        final File nonmediaFile = new File(getExternalFilesDir(), NONMEDIA_FILE_NAME);
 
         try {
             // Create a file in app's external files directory
-            if (!videoFile.exists()) {
-                assertThat(videoFile.createNewFile()).isTrue();
+            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(videoFile.getParentFile(), videoFile);
+            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()));
+            // TODO(b/157650550): we don't have consistent behaviour on both primary and public
+            //  volumes
+//            assertThrows(IOException.class,
+//                    () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
             assertThrows(IOException.class,
                     () -> listAs(TEST_APP_A, getExternalFilesDir().getPath()));
         } finally {
-            videoFile.delete();
+            nonmediaFile.delete();
             uninstallAppNoThrow(TEST_APP_A);
         }
     }