Merge "Add MTS suite config" into mainline-prod
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 305027b..4fe4843 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,6 +25,9 @@
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
 
+    <!-- Permissions required for statsd pull metrics -->
+    <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM"/>
+
     <application
             android:name="com.android.providers.media.MediaApplication"
             android:label="@string/app_label"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 93218cf..3af81a2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -31,6 +31,9 @@
             "name": "CtsScopedStorageHostTest"
         },
         {
+            "name": "CtsScopedStorageDeviceOnlyTest"
+        },
+        {
             "name": "fuse_node_test"
         }
     ],
diff --git a/jni/MediaProviderWrapper.cpp b/jni/MediaProviderWrapper.cpp
index fb72961..734ae97 100644
--- a/jni/MediaProviderWrapper.cpp
+++ b/jni/MediaProviderWrapper.cpp
@@ -116,13 +116,6 @@
     return res;
 }
 
-void scanFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_scan_file,
-                      const string& path) {
-    ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
-    env->CallVoidMethod(media_provider_object, mid_scan_file, j_path.get());
-    CheckForJniException(env);
-}
-
 int isMkdirOrRmdirAllowedInternal(JNIEnv* env, jobject media_provider_object,
                                   jmethodID mid_is_mkdir_or_rmdir_allowed, const string& path,
                                   uid_t uid, bool forCreate) {
@@ -267,8 +260,6 @@
     mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I", /*is_static*/ false);
     mid_is_open_allowed_ = CacheMethod(env, "isOpenAllowed", "(Ljava/lang/String;IZ)I",
                                        /*is_static*/ false);
-    mid_scan_file_ = CacheMethod(env, "scanFile", "(Ljava/lang/String;)V",
-                                 /*is_static*/ false);
     mid_is_mkdir_or_rmdir_allowed_ = CacheMethod(env, "isDirectoryCreationOrDeletionAllowed",
                                                  "(Ljava/lang/String;IZ)I", /*is_static*/ false);
     mid_is_opendir_allowed_ = CacheMethod(env, "isOpendirAllowed", "(Ljava/lang/String;IZ)I",
@@ -341,11 +332,6 @@
                                  for_write);
 }
 
-void MediaProviderWrapper::ScanFile(const string& path) {
-    JNIEnv* env = MaybeAttachCurrentThread();
-    scanFileInternal(env, media_provider_object_, mid_scan_file_, path);
-}
-
 int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
     if (shouldBypassMediaProvider(uid)) {
         return 0;
diff --git a/jni/MediaProviderWrapper.h b/jni/MediaProviderWrapper.h
index ce5a5b1..23c611a 100644
--- a/jni/MediaProviderWrapper.h
+++ b/jni/MediaProviderWrapper.h
@@ -101,18 +101,6 @@
     int IsOpenAllowed(const std::string& path, uid_t uid, bool for_write);
 
     /**
-     * Potentially triggers a scan of the file before closing it and reconciles it with the
-     * MediaProvider database.
-     *
-     * @param path the path of the file to be scanned
-     */
-    void ScanFile(const std::string& path);
-
-    /**
-     * Determines if the given UID is allowed to create a directory with the given path.
-     *
-     * @param path the path of the directory to be created
-     * @param uid UID of the calling app
      * @return 0 if it's allowed, or errno error code if operation isn't allowed.
      */
     int IsCreatingDirAllowed(const std::string& path, uid_t uid);
@@ -202,7 +190,6 @@
     jmethodID mid_insert_file_;
     jmethodID mid_delete_file_;
     jmethodID mid_is_open_allowed_;
-    jmethodID mid_scan_file_;
     jmethodID mid_is_mkdir_or_rmdir_allowed_;
     jmethodID mid_is_opendir_allowed_;
     jmethodID mid_get_files_in_dir_;
diff --git a/src/com/android/providers/media/DatabaseHelper.java b/src/com/android/providers/media/DatabaseHelper.java
index faa6d18..9c67aa6 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -876,9 +876,17 @@
                     final String data = c.getString(c.getColumnIndex(MediaColumns.DATA));
                     values.put(MediaColumns.DATA, data);
                     FileUtils.computeValuesFromData(values, /*isForFuse*/ false);
+                    final String volumeNameFromPath = values.getAsString(MediaColumns.VOLUME_NAME);
                     for (String column : sMigrateColumns) {
                         DatabaseUtils.copyFromCursorToContentValues(column, c, values);
                     }
+                    final String volumeNameMigrated = values.getAsString(MediaColumns.VOLUME_NAME);
+                    // While upgrading from P OS or below, VOLUME_NAME can be NULL in legacy
+                    // database. When VOLUME_NAME is NULL, extract VOLUME_NAME from
+                    // MediaColumns.DATA
+                    if (volumeNameMigrated == null || volumeNameMigrated.isEmpty()) {
+                        values.put(MediaColumns.VOLUME_NAME, volumeNameFromPath);
+                    }
 
                     final String volumePath = FileUtils.extractVolumePath(data);
 
@@ -905,7 +913,7 @@
                             } catch (Exception e) {
                                 // We only have one shot to migrate data, so log and
                                 // keep marching forward.
-                                Log.wtf(TAG, "Couldn't migrate playlist file " + data);
+                                Log.w(TAG, "Couldn't migrate playlist file " + data);
                             }
 
                             values.put(FileColumns.DATA, playlistFilePath);
@@ -926,7 +934,7 @@
                             } catch (IOException e) {
                                 // We only have one shot to migrate data, so log and
                                 // keep marching forward
-                                Log.wtf(TAG, "Failed to rename " + values + "; continuing", e);
+                                Log.w(TAG, "Failed to rename " + values + "; continuing", e);
                                 FileUtils.computeValuesFromData(values, /*isForFuse*/ false);
                             }
                         }
@@ -957,7 +965,7 @@
             } catch (Exception e) {
                 // We have to guard ourselves against any weird behavior of the
                 // legacy provider by trying to catch everything
-                Log.wtf(TAG, "Failed migration from legacy provider", e);
+                Log.w(TAG, "Failed migration from legacy provider", e);
             }
 
             // We tried our best above to migrate everything we could, and we
@@ -1076,7 +1084,7 @@
             } catch (IOException e) {
                 // We only have one shot to migrate data, so log and
                 // keep marching forward.
-                Log.wtf(TAG, "Couldn't migrate playlist file " + playlistFile);
+                Log.w(TAG, "Couldn't migrate playlist file " + playlistFile);
             }
         } catch (RemoteException e) {
             throw new IllegalStateException(e);
@@ -1092,7 +1100,7 @@
                 return c.getString(0);
             }
         } catch (Exception e) {
-            Log.wtf(TAG, "Exception occurred while querying for data file for " + uri, e);
+            Log.w(TAG, "Exception occurred while querying for data file for " + uri, e);
         }
         return null;
     }
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 4166234..9e1efa5 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -1220,17 +1220,6 @@
     }
 
     /**
-     * Makes MediaScanner scan the given file.
-     * @param file path of the file to be scanned
-     *
-     * Called from JNI in jni/MediaProviderWrapper.cpp
-     */
-    @Keep
-    public void scanFileForFuse(String file) {
-        scanFile(new File(file), REASON_DEMAND);
-    }
-
-    /**
      * Called when a new file is created through FUSE
      *
      * @param file path of the file that was created
@@ -1756,13 +1745,8 @@
      * </ul>
      */
     private void scanRenamedDirectoryForFuse(@NonNull String oldPath, @NonNull String newPath) {
-        final LocalCallingIdentity token = clearLocalCallingIdentity();
-        try {
-            scanFile(new File(oldPath), REASON_DEMAND);
-            scanFile(new File(newPath), REASON_DEMAND);
-        } finally {
-            restoreLocalCallingIdentity(token);
-        }
+        scanFileAsMediaProvider(new File(oldPath), REASON_DEMAND);
+        scanFileAsMediaProvider(new File(newPath), REASON_DEMAND);
     }
 
     /**
@@ -2213,10 +2197,10 @@
         // 3) /sdcard/foo/bar.mp3 => /sdcard/foo/.nomedia
         //    in this case, we need to scan all of /sdcard/foo
         if (extractDisplayName(oldPath).equals(".nomedia")) {
-            scanFile(new File(oldPath).getParentFile(), REASON_DEMAND);
+            scanFileAsMediaProvider(new File(oldPath).getParentFile(), REASON_DEMAND);
         }
         if (extractDisplayName(newPath).equals(".nomedia")) {
-            scanFile(new File(newPath).getParentFile(), REASON_DEMAND);
+            scanFileAsMediaProvider(new File(newPath).getParentFile(), REASON_DEMAND);
         }
 
         return 0;
@@ -3891,7 +3875,7 @@
         mCallingIdentity.get().setOwned(rowId, true);
 
         if (path != null && path.toLowerCase(Locale.ROOT).endsWith("/.nomedia")) {
-            mMediaScanner.scanFile(new File(path).getParentFile(), REASON_DEMAND);
+            scanFileAsMediaProvider(new File(path).getParentFile(), REASON_DEMAND);
         }
 
         return newUri;
@@ -4788,8 +4772,10 @@
                 // Do this on a background thread, since we don't want to make binder
                 // calls as part of a FUSE call.
                 helper.postBackground(() -> {
-                    getContext().getSystemService(DownloadManager.class)
-                            .onMediaStoreDownloadsDeleted(deletedDownloadIds);
+                    DownloadManager dm = getContext().getSystemService(DownloadManager.class);
+                    if (dm != null) {
+                        dm.onMediaStoreDownloadsDeleted(deletedDownloadIds);
+                    }
                 });
             }
 
@@ -6595,7 +6581,7 @@
                         update(uri, values, null, null);
                         break;
                     default:
-                        mMediaScanner.scanFile(file, REASON_DEMAND);
+                        scanFileAsMediaProvider(file, REASON_DEMAND);
                         break;
                 }
             } catch (Exception e2) {
diff --git a/src/com/android/providers/media/PermissionActivity.java b/src/com/android/providers/media/PermissionActivity.java
index 0183690..23c3f90 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -189,17 +189,6 @@
 
         actionDialog = builder.show();
 
-        // The title is being set as a message above.
-        // We need to style it like the default AlertDialog title
-        TextView dialogMessage = (TextView) actionDialog.findViewById(
-                android.R.id.message);
-        if (dialogMessage != null) {
-            dialogMessage.setTextAppearance(
-                    android.R.style.TextAppearance_DeviceDefault_DialogWindowTitle);
-        } else {
-            Log.w(TAG, "Couldn't find message element");
-        }
-
         final WindowManager.LayoutParams params = actionDialog.getWindow().getAttributes();
         params.width = getResources().getDimensionPixelSize(R.dimen.permission_dialog_width);
         actionDialog.getWindow().setAttributes(params);
diff --git a/src/com/android/providers/media/util/IsoInterface.java b/src/com/android/providers/media/util/IsoInterface.java
index 5de4b6b..03b46c9 100644
--- a/src/com/android/providers/media/util/IsoInterface.java
+++ b/src/com/android/providers/media/util/IsoInterface.java
@@ -177,14 +177,25 @@
                 return null;
             }
 
-            box.data = new byte[(int) (len - box.headerSize)];
+            try {
+                box.data = new byte[(int) (len - box.headerSize)];
+            } catch (OutOfMemoryError e) {
+                Log.w(TAG, "Couldn't read large uuid box", e);
+                return null;
+            }
             Os.read(fd, box.data, 0, box.data.length);
         } else if (type == BOX_XMP) {
             if (len > Integer.MAX_VALUE) {
                 Log.w(TAG, "Skipping abnormally large xmp box");
                 return null;
             }
-            box.data = new byte[(int) (len - box.headerSize)];
+
+            try {
+                box.data = new byte[(int) (len - box.headerSize)];
+            } catch (OutOfMemoryError e) {
+                Log.w(TAG, "Couldn't read large xmp box", e);
+                return null;
+            }
             Os.read(fd, box.data, 0, box.data.length);
         } else if (type == BOX_META && len != headerSize) {
             // The format of this differs in ISO and QT encoding:
diff --git a/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java b/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
index 8014b35..831aa9b 100644
--- a/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
+++ b/tests/client/src/com/android/providers/media/client/LegacyProviderMigrationTest.java
@@ -207,6 +207,18 @@
         doLegacy(mExternalDownloads, values);
     }
 
+    /**
+     * Test that migrating from legacy database with volume_name=NULL doesn't
+     * result in empty cursor when queried.
+     */
+    @Test
+    public void testMigrateNullVolumeName() throws Exception {
+        final ContentValues values = generateValues(FileColumns.MEDIA_TYPE_IMAGE,
+                "image/png", Environment.DIRECTORY_PICTURES);
+        values.remove(MediaColumns.VOLUME_NAME);
+        doLegacy(mExternalImages, values);
+    }
+
     @Test
     public void testLegacy_PlaylistMap() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
diff --git a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
index 277acf2..e4d2e52 100644
--- a/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderForFuseTest.java
@@ -138,13 +138,6 @@
     }
 
     @Test
-    public void test_scanFileForFuse() throws Exception {
-        final File file = new File(sTestDir, "test" + System.nanoTime() + ".jpg");
-        Truth.assertThat(file.createNewFile()).isTrue();
-        sMediaProvider.scanFileForFuse(file.getPath());
-    }
-
-    @Test
     public void test_isOpendirAllowedForFuse() throws Exception {
         Truth.assertThat(sMediaProvider.isOpendirAllowedForFuse(
                 sTestDir.getPath(), sTestUid, /* forWrite */ false)).isEqualTo(0);