Grant system gallery read access to MediaProvider tables

Allows system gallery apps to query any image or video file.
This is the missing piece that allows system galleries to have full
access to image and video files throuh MediaProvider.
System galleries had this level of access through file path access. This
CL only extends this capability to ContentResolver and MediaStore APIs.

Test: atest FuseDaemonHostTest
Fixes: 149733996
Change-Id: I5a3cf7217cd60c02566c1d483e7b7672e8db6d05
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 023d723..8e10bb9 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -5696,7 +5696,9 @@
         if (forWrite) {
             return mCallingIdentity.get().hasPermission(PERMISSION_WRITE_AUDIO);
         } else {
-            return mCallingIdentity.get().hasPermission(PERMISSION_READ_AUDIO);
+            // write permission should be enough for reading as well
+            return mCallingIdentity.get().hasPermission(PERMISSION_READ_AUDIO)
+                    || mCallingIdentity.get().hasPermission(PERMISSION_WRITE_AUDIO);
         }
     }
 
@@ -5705,7 +5707,9 @@
         if (forWrite) {
             return mCallingIdentity.get().hasPermission(PERMISSION_WRITE_VIDEO);
         } else {
-            return mCallingIdentity.get().hasPermission(PERMISSION_READ_VIDEO);
+            // write permission should be enough for reading as well
+            return mCallingIdentity.get().hasPermission(PERMISSION_READ_VIDEO)
+                    || mCallingIdentity.get().hasPermission(PERMISSION_WRITE_VIDEO);
         }
     }
 
@@ -5714,7 +5718,9 @@
         if (forWrite) {
             return mCallingIdentity.get().hasPermission(PERMISSION_WRITE_IMAGES);
         } else {
-            return mCallingIdentity.get().hasPermission(PERMISSION_READ_IMAGES);
+            // write permission should be enough for reading as well
+            return mCallingIdentity.get().hasPermission(PERMISSION_READ_IMAGES)
+                    || mCallingIdentity.get().hasPermission(PERMISSION_WRITE_IMAGES);
         }
     }
 
diff --git a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java
index b3ee227..eea937d 100644
--- a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java
+++ b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java
@@ -228,4 +228,14 @@
     public void testManageExternalStorageQueryOtherAppsFile() throws Exception {
         runDeviceTest("testManageExternalStorageQueryOtherAppsFile");
     }
+
+    @Test
+    public void testSystemGalleryQueryOtherAppsFiles() throws Exception {
+        runDeviceTest("testSystemGalleryQueryOtherAppsFiles");
+    }
+
+    @Test
+    public void testQueryOtherAppsFiles() throws Exception {
+        runDeviceTest("testQueryOtherAppsFiles");
+    }
 }
diff --git a/tests/jni/FuseDaemonTest/src/com/android/tests/fused/FilePathAccessTest.java b/tests/jni/FuseDaemonTest/src/com/android/tests/fused/FilePathAccessTest.java
index d9b0fe6..8c8ee92 100644
--- a/tests/jni/FuseDaemonTest/src/com/android/tests/fused/FilePathAccessTest.java
+++ b/tests/jni/FuseDaemonTest/src/com/android/tests/fused/FilePathAccessTest.java
@@ -1396,7 +1396,7 @@
             installApp(TEST_APP_A, false);
             assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
 
-            // Once we have permission to manage external storage, we can query for other apps'
+            // Once the test has permission to manage external storage, it can query for other apps'
             // files and open them for read and write
             adoptShellPermissionIdentity(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
 
@@ -1410,6 +1410,53 @@
         }
     }
 
+    @Test
+    public void testQueryOtherAppsFiles() throws Exception {
+        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(MUSIC_DIR, "other" + MUSIC_FILE_NAME);
+        try {
+            installApp(TEST_APP_A, false);
+            assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+
+            // 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);
+        } finally {
+            deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+            uninstallApp(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testSystemGalleryQueryOtherAppsFiles() throws Exception {
+        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(MUSIC_DIR, "other" + MUSIC_FILE_NAME);
+        try {
+            installApp(TEST_APP_A, false);
+            assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+
+            // System gallery apps have access to video and image files
+            allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+            assertCanQueryAndOpenFile(otherAppImg, "rw");
+            // 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);
+            uninstallApp(TEST_APP_A);
+        }
+    }
+
+    private static void assertCantQueryFile(File file) {
+        assertThat(getFileUri(file)).isNull();
+    }
+
     private static void assertCreateFilesAs(TestApp testApp, File... files) throws Exception {
         for (File file : files) {
             assertThat(createFileAs(testApp, file.getPath())).isTrue();
@@ -1422,6 +1469,9 @@
         }
     }
 
+    /**
+     * 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);